SlideShare a Scribd company logo
1 of 39
The Gradle in
Ratpack: Dissected
Presented by David Carr
Gradle Summit 2016 - June 22
GitHub: davidmc24
Twitter: @varzof
Who Am I?
• Systems Architect at
CommerceHub
• Java/Groovy Developer
• Gradle Enthusiast
• Ratpack Contributor
What is Ratpack?
• A web framework
• A set of Java libraries
• Purely a runtime
• No installable package
• No coupled/required build tooling
• Gradle recommended; plugins provided
Ratpack Hello World
Using the ratpack-groovy Gradle plugin
build.gradle
plugins {
id "io.ratpack.ratpack-groovy" 
version "1.3.3"
}
repositories {
jcenter()
}
dependencies {
runtime "org.slf4j:slf4j-simple:1.7.21"
}
src/ratpack/ratpack.groovy
import static 
ratpack.groovy.Groovy.ratpack
ratpack {
handlers {
get {
render "Hello world!n"
}
}
}
gradle run
$ gradle run
:compileJava UP-TO-DATE
:compileGroovy UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:configureRun
:run
[main] INFO ratpack.server.RatpackServer -
Starting server...
[main] INFO ratpack.server.RatpackServer -
Building registry...
[main] INFO ratpack.server.RatpackServer -
Ratpack started (development) for
http://localhost:5050
gradle run --continuous
$ gradle run --continuous --quiet
[main] INFO ratpack.server.RatpackServer - Starting server...
[main] INFO ratpack.server.RatpackServer - Building
registry...
[main] INFO ratpack.server.RatpackServer - Ratpack started
(development) for http://localhost:5050
[main] INFO ratpack.server.RatpackServer - Stopping server...
[main] INFO ratpack.server.RatpackServer - Server stopped.
[main] INFO ratpack.server.RatpackServer - Starting server...
[main] INFO ratpack.server.RatpackServer - Building
registry...
[main] INFO ratpack.server.RatpackServer - Ratpack started
(development) for http://localhost:5050
$
Ratpack Gradle Plugins
Base, Java, Groovy
RatpackBasePlugin
class RatpackBasePlugin
implements Plugin<Project> {
@Override
void apply(Project project) {
project.extensions.create(
"ratpack", RatpackExtension,
project)
}
}
RatpackPlugin
private static final GradleVersion
GRADLE_VERSION_BASELINE =
GradleVersion.version("2.6")
…
void apply(Project project) {
def gradleVersion = GradleVersion.version(
project.gradle.gradleVersion)
if (gradleVersion < GRADLE_VERSION_BASELINE) {
throw new GradleException(
"Ratpack requires Gradle version
${GRADLE_VERSION_BASELINE.version}
or later")
}
RatpackPlugin
project.plugins.apply(ApplicationPlugin)
project.plugins.apply(RatpackBasePlugin)
RatpackExtension ratpackExtension =
project.extensions.findByType(
RatpackExtension)
SourceSetContainer sourceSets =
project.sourceSets
def mainSourceSet =
sourceSets[SourceSet.MAIN_SOURCE_SET_NAME]
mainSourceSet.resources.srcDir {
ratpackExtension.baseDir }
RatpackPlugin
project.dependencies {
compile ratpackExtension.core
testCompile ratpackExtension.test
}
RatpackExtension
class RatpackExtension {
…
private final
DependencyHandler dependencies
File baseDir
RatpackExtension(Project project) {
this.dependencies =
project.dependencies
baseDir = project.file('src/ratpack')
}
RatpackExtension
class RatpackExtension {
public static final String GROUP = "io.ratpack"
private final version =
getClass().classLoader.getResource(
"ratpack/ratpack-version.txt").text.trim()
…
Dependency getGroovyTest() {
dependency("groovy-test")
}
Dependency dependency(String name) {
dependencies.create(
"${GROUP}:ratpack-${name}:${version}")
}
}
RatpackPlugin
JavaExec runTask =
project.tasks.findByName("run") as JavaExec
def configureRun =
project.task("configureRun")
configureRun.doFirst {
runTask.with {
systemProperty "ratpack.development", true
}
}
runTask.dependsOn configureRun
RatpackGroovyPlugin
class RatpackGroovyPlugin
implements Plugin<Project> {
@Override
void apply(Project project) {
project.plugins.apply(RatpackPlugin)
project.plugins.apply(GroovyPlugin)
project.mainClassName =
"ratpack.groovy.GroovyRatpackMain"
…
RatpackGroovyPlugin
…
def ratpackExtension =
project.extensions
.getByType(RatpackExtension)
project.dependencies {
compile ratpackExtension.groovy
testCompile
ratpackExtension.groovyTest
}
}
}
Plugin Testing
Using Spock and Gradle TestKit
DslSpec
class DslSpec extends FunctionalSpec {
def "can use extension to add dependencies"() {
given:
buildFile << """
configurations.all {
transitive = false // we don't need jackson itself for this test
}
dependencies {
compile ratpack.dependency("jackson-guice")
}
task showDeps {
doLast {
file("deps.txt") << configurations.compile.dependencies*.toString().join('n')
}
}
"""
when:
run "showDeps"
then:
file("deps.txt").text.contains("ratpack-jackson-guice")
}
}
InstallDistSpec
def "everything goes in the right place"() {
given:
file("src/ratpack/ratpack.groovy") << """
import static ratpack.groovy.Groovy.*
import ratpack.server.Stopper
ratpack {
serverConfig { port 0 }
handlers {
get {
onClose { get(Stopper).stop() }
render "foo"
}
get("stop") {
onClose { get(ratpack.server.Stopper).stop() }
}
}
}
"""
InstallDistSpec
when:
run "installDist"
def process = new ProcessBuilder()
.directory(file("build/install/test-app"))
.command(osSpecificCommand())
.start()
def port = scrapePort(process)
then:
new PollingConditions().within(10) {
try {
urlText(port) == "foo"
} catch (ConnectException ignore) {
false
}
}
cleanup:
if (port) {
url(port, "stop")
}
process?.destroy()
process?.waitFor()
}
FunctionalSpec
abstract class FunctionalSpec extends Specification {
GradleRunner runner(String... args) {
GradleRunner.create()
.withProjectDir(dir.root)
.withDebug(true)
.forwardOutput()
.withTestKitDir(getTestKitDir())
.withArguments(args.toList())
}
BuildResult run(String... args) {
runner(args).build()
}
BuildResult fail(String... args) {
runner(args).buildAndFail()
}
FunctionalSpec
File getBuildFile() {
makeFile("build.gradle")
}
File makeFile(String path) {
def f = file(path)
if (!f.exists()) {
def parts = path.split("/")
if (parts.size() > 1) {
dir.newFolder(*parts[0..-2])
}
dir.newFile(path)
}
f
}
File file(String path) {
def file = new File(dir.root, path)
assert file.parentFile.mkdirs() || file.parentFile.exists()
file
}
FunctionalSpec
def setup() {
file("settings.gradle") << "rootProject.name = 'test-app'"
buildFile << """
buildscript {
repositories {
maven { url "${localRepo.toURI()}" }
jcenter()
}
repositories { jcenter() }
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3'
classpath 'io.ratpack:ratpack-gradle:${RATPACK_VERSION}'
}
}
apply plugin: 'io.ratpack.ratpack-groovy'
apply plugin: 'com.github.johnrengelman.shadow'
version = "1.0"
repositories {
maven { url "${localRepo.toURI()}" }
jcenter()
}
dependencies { compile 'org.slf4j:slf4j-simple:1.7.12' }
"""
file("src/ratpack/ratpack.properties") << "port=0n"
}
FunctionalSpec
int scrapePort(Process process) {
int port = -1
def latch = new CountDownLatch(1)
Thread.start {
process.errorStream.eachLine { String line ->
System.err.println(line)
if (latch.count) {
if (line.contains("Ratpack started for http://localhost:")) {
def matcher = (line =~ "http://localhost:(d+)")
port = matcher[0][1].toString().toInteger()
latch.countDown()
}
}
}
}
if (!latch.await(15, TimeUnit.SECONDS)) {
throw new RuntimeException("Timeout waiting for application to start")
}
port
}
Ratpack Project Build
Build File Naming
def setBuildFile(project) {
project.buildFileName =
"${project.name}.gradle"
project.children.each {
setBuildFile(it)
}
}
setBuildFile(rootProject)
Build Logic Re-use
Usage:
apply from: 
"$rootDir/gradle/javaModule.gradle"
Extracted build files:
checkstyle, codenarc, dependencies,
dependencyRules, groovyModule, javaModule,
markdown2book, pom, projectLocalRepo, publish,
ratpackLocal, versionFile
Version as a Resource
task writeVersionNumberFile {
def versionFile = file("src/main/resources/ratpack/ratpack-version.txt")
inputs.property "version", project.version
outputs.file versionFile
doFirst {
assert versionFile.parentFile.mkdirs()
|| versionFile.parentFile.directory
versionFile.text = project.version
}
}
processResources.dependsOn writeVersionNumberFile
plugins.withType(EclipsePlugin) {
eclipseProject.dependsOn writeVersionNumberFile
}
plugins.withType(IdeaPlugin) {
ideaModule.dependsOn writeVersionNumberFile
}
Minimum Groovy
Version
processResources {
inputs.property("groovyVersion",
commonVersions.groovy)
def minimumGroovyVersionFile =
file("src/main/resources/ratpack/
minimum-groovy-version.txt")
outputs.file minimumGroovyVersionFile
doFirst {
minimumGroovyVersionFile.parentFile.mkdirs()
minimumGroovyVersionFile.text =
commonVersions.groovy
}
}
Java Settings
def jvmEncoding =
java.nio.charset.Charset.defaultCharset().name()
if (jvmEncoding != "UTF-8") {
throw new IllegalStateException(
"Build environment must be UTF-8 (it is:
$jvmEncoding) - add '-Dfile.encoding=UTF-8'
to the GRADLE_OPTS environment variable ")
}
if (!JavaVersion.current().java8Compatible) {
throw new IllegalStateException(
"Must be built with Java 8 or higher")
}
Download Dependencies
task downloadDependencies << {
allprojects { p ->
configurations.each { c ->
println "Downloading dependencies
for $p.path - $c.name"
c.files
}
}
}
Version Management
ext {
commonVersions = [
netty: "4.1.1.Final",
slf4j: "1.7.12",
spock: "1.0-groovy-2.4",
]
commonDependencies = [
slf4j: "org.slf4j:slf4j-api:${commonVersions.slf4j}",
spock: dependencies.create(
"org.spockframework:spock-core:${commonVersions.spock}", {
exclude group: "org.codehaus.groovy", module: "groovy-all"
}),
]
…
compile commonDependencies.slf4j
compile "io.netty:netty-codec-http:$commonVersions.netty"
compile "io.netty:netty-handler:$commonVersions.netty"
Alternate Groovy
Version
if (System.getenv('CI_GROOVY_VERSION')) {
commonVersions.groovy =
System.getenv('CI_GROOVY_VERSION')
if (commonVersions.groovy.endsWith('-SNAPSHOT')) {
allprojects {
repositories {
maven {
name 'JFrog OSS snapshot repo'
url 'https://oss.jfrog.org/oss-snapshot-local/'
}
}
}
}
}
Cloud CI Detection
ext {
isTravis = System.getenv("TRAVIS") != null
isDrone = System.getenv("DRONE") != null
isCodeship =
System.getenv("CI_NAME") == "codeship"
isSnapCi = System.getenv("SNAP_CI") != null
isAppveyor = System.getenv("APPVEYOR") != null
isHeroku = System.getenv("HEROKU") != null
isCloudCi = isTravis || isDrone || isCodeship
|| isSnapCi || isAppveyor
isCi = isCloudCi
}
Memory Usage
test {
maxParallelForks = 3
}
tasks.withType(GroovyCompile) {
groovyOptions.forkOptions.memoryMaximumSize = "512m"
}
tasks.withType(Test) {
jvmArgs "-Xss320k"
minHeapSize "120m"
maxHeapSize "280m"
doFirst {
if (isCloudCi) {
maxParallelForks 1
systemProperty "cloudCi", "true"
}
}
}
Command-line Options
In Snap CI:
GRADLE_OPTS=
-Xmx386m
-Xms386m
-Dorg.gradle.parallel.intra=true
./gradlew check
--parallel
--max-workers=2
--gradle-user-home ${SNAP_CACHE_DIR}/.gradle
Questions?
P.S. We're Hiring!
David M. Carr
Systems Architect
GitHub: davidmc24
Twitter: @varzof

More Related Content

What's hot

Groovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume LaforgeGroovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume LaforgeGuillaume Laforge
 
Apache Groovy: the language and the ecosystem
Apache Groovy: the language and the ecosystemApache Groovy: the language and the ecosystem
Apache Groovy: the language and the ecosystemKostas Saidis
 
Open Source tools overview
Open Source tools overviewOpen Source tools overview
Open Source tools overviewLuciano Resende
 
GDG Cloud Taipei: Meetup #52 - Istio Security: API Authorization
GDG Cloud Taipei: Meetup #52 - Istio Security: API AuthorizationGDG Cloud Taipei: Meetup #52 - Istio Security: API Authorization
GDG Cloud Taipei: Meetup #52 - Istio Security: API AuthorizationKAI CHU CHUNG
 
Test Driven Documentation with Spring Rest Docs JEEConf2017
Test Driven Documentation with Spring Rest Docs JEEConf2017Test Driven Documentation with Spring Rest Docs JEEConf2017
Test Driven Documentation with Spring Rest Docs JEEConf2017Roman Tsypuk
 
Groovy And Grails JUG Padova
Groovy And Grails JUG PadovaGroovy And Grails JUG Padova
Groovy And Grails JUG PadovaJohn Leach
 
Groovy & Grails: Scripting for Modern Web Applications
Groovy & Grails: Scripting for Modern Web ApplicationsGroovy & Grails: Scripting for Modern Web Applications
Groovy & Grails: Scripting for Modern Web Applicationsrohitnayak
 
Ratpack - Classy and Compact Groovy Web Apps
Ratpack - Classy and Compact Groovy Web AppsRatpack - Classy and Compact Groovy Web Apps
Ratpack - Classy and Compact Groovy Web AppsJames Williams
 
Gradle: The Build System you have been waiting for!
Gradle: The Build System you have been waiting for!Gradle: The Build System you have been waiting for!
Gradle: The Build System you have been waiting for!Corneil du Plessis
 
Gradle - time for a new build
Gradle - time for a new buildGradle - time for a new build
Gradle - time for a new buildIgor Khotin
 
Functional Reactive Programming on Android
Functional Reactive Programming on AndroidFunctional Reactive Programming on Android
Functional Reactive Programming on AndroidSam Lee
 
In the Brain of Hans Dockter: Gradle
In the Brain of Hans Dockter: GradleIn the Brain of Hans Dockter: Gradle
In the Brain of Hans Dockter: GradleSkills Matter
 
Testing of javacript
Testing of javacriptTesting of javacript
Testing of javacriptLei Kang
 
Java11 New Features
Java11 New FeaturesJava11 New Features
Java11 New FeaturesHaim Michael
 

What's hot (20)

Groovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume LaforgeGroovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
 
Apache Groovy: the language and the ecosystem
Apache Groovy: the language and the ecosystemApache Groovy: the language and the ecosystem
Apache Groovy: the language and the ecosystem
 
Gradle in 45min
Gradle in 45minGradle in 45min
Gradle in 45min
 
Open Source tools overview
Open Source tools overviewOpen Source tools overview
Open Source tools overview
 
GDG Cloud Taipei: Meetup #52 - Istio Security: API Authorization
GDG Cloud Taipei: Meetup #52 - Istio Security: API AuthorizationGDG Cloud Taipei: Meetup #52 - Istio Security: API Authorization
GDG Cloud Taipei: Meetup #52 - Istio Security: API Authorization
 
groovy & grails - lecture 10
groovy & grails - lecture 10groovy & grails - lecture 10
groovy & grails - lecture 10
 
Test Driven Documentation with Spring Rest Docs JEEConf2017
Test Driven Documentation with Spring Rest Docs JEEConf2017Test Driven Documentation with Spring Rest Docs JEEConf2017
Test Driven Documentation with Spring Rest Docs JEEConf2017
 
Groovy And Grails JUG Padova
Groovy And Grails JUG PadovaGroovy And Grails JUG Padova
Groovy And Grails JUG Padova
 
Groovy & Grails: Scripting for Modern Web Applications
Groovy & Grails: Scripting for Modern Web ApplicationsGroovy & Grails: Scripting for Modern Web Applications
Groovy & Grails: Scripting for Modern Web Applications
 
Rest, sockets em golang
Rest, sockets em golangRest, sockets em golang
Rest, sockets em golang
 
Ratpack - Classy and Compact Groovy Web Apps
Ratpack - Classy and Compact Groovy Web AppsRatpack - Classy and Compact Groovy Web Apps
Ratpack - Classy and Compact Groovy Web Apps
 
Gradle: The Build System you have been waiting for!
Gradle: The Build System you have been waiting for!Gradle: The Build System you have been waiting for!
Gradle: The Build System you have been waiting for!
 
Gradle - time for a new build
Gradle - time for a new buildGradle - time for a new build
Gradle - time for a new build
 
Ratpack JVM_MX Meetup February 2016
Ratpack JVM_MX Meetup February 2016Ratpack JVM_MX Meetup February 2016
Ratpack JVM_MX Meetup February 2016
 
Grooscript and Grails 3
Grooscript and Grails 3Grooscript and Grails 3
Grooscript and Grails 3
 
groovy & grails - lecture 9
groovy & grails - lecture 9groovy & grails - lecture 9
groovy & grails - lecture 9
 
Functional Reactive Programming on Android
Functional Reactive Programming on AndroidFunctional Reactive Programming on Android
Functional Reactive Programming on Android
 
In the Brain of Hans Dockter: Gradle
In the Brain of Hans Dockter: GradleIn the Brain of Hans Dockter: Gradle
In the Brain of Hans Dockter: Gradle
 
Testing of javacript
Testing of javacriptTesting of javacript
Testing of javacript
 
Java11 New Features
Java11 New FeaturesJava11 New Features
Java11 New Features
 

Similar to The Gradle in Ratpack: Dissected

High Performance Microservices with Ratpack and Spring Boot
High Performance Microservices with Ratpack and Spring BootHigh Performance Microservices with Ratpack and Spring Boot
High Performance Microservices with Ratpack and Spring BootDaniel Woods
 
Construire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradleConstruire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradleThierry Wasylczenko
 
TDC2018SP | Trilha Go - Processando analise genetica em background com Go
TDC2018SP | Trilha Go - Processando analise genetica em background com GoTDC2018SP | Trilha Go - Processando analise genetica em background com Go
TDC2018SP | Trilha Go - Processando analise genetica em background com Gotdc-globalcode
 
Node.js - async for the rest of us.
Node.js - async for the rest of us.Node.js - async for the rest of us.
Node.js - async for the rest of us.Mike Brevoort
 
Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01Tino Isnich
 
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
名古屋SGGAE/J勉強会 Grails、GaelykでハンズオンTsuyoshi Yamamoto
 
Soft-Shake 2016 : Jigsaw est prêt à tuer le classpath
Soft-Shake 2016 : Jigsaw  est prêt à tuer le classpathSoft-Shake 2016 : Jigsaw  est prêt à tuer le classpath
Soft-Shake 2016 : Jigsaw est prêt à tuer le classpathAlexis Hassler
 
Groovy Ecosystem - JFokus 2011 - Guillaume Laforge
Groovy Ecosystem - JFokus 2011 - Guillaume LaforgeGroovy Ecosystem - JFokus 2011 - Guillaume Laforge
Groovy Ecosystem - JFokus 2011 - Guillaume LaforgeGuillaume Laforge
 
Jenkins and Groovy
Jenkins and GroovyJenkins and Groovy
Jenkins and GroovyKiyotaka Oku
 
Puppet Module Reusability - What I Learned from Shipping to the Forge
Puppet Module Reusability - What I Learned from Shipping to the ForgePuppet Module Reusability - What I Learned from Shipping to the Forge
Puppet Module Reusability - What I Learned from Shipping to the ForgePuppet
 
OSGi ecosystems compared on Apache Karaf - Christian Schneider
OSGi ecosystems compared on Apache Karaf - Christian SchneiderOSGi ecosystems compared on Apache Karaf - Christian Schneider
OSGi ecosystems compared on Apache Karaf - Christian Schneidermfrancis
 
Dropwizard and Friends
Dropwizard and FriendsDropwizard and Friends
Dropwizard and FriendsYun Zhi Lin
 
Grunt & Front-end Workflow
Grunt & Front-end WorkflowGrunt & Front-end Workflow
Grunt & Front-end WorkflowPagepro
 
こわくないよ❤️ Playframeworkソースコードリーディング入門
こわくないよ❤️ Playframeworkソースコードリーディング入門こわくないよ❤️ Playframeworkソースコードリーディング入門
こわくないよ❤️ Playframeworkソースコードリーディング入門tanacasino
 
Oredev 2015 - Taming Java Agents
Oredev 2015 - Taming Java AgentsOredev 2015 - Taming Java Agents
Oredev 2015 - Taming Java AgentsAnton Arhipov
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing UpDavid Padbury
 
Android Automated Testing
Android Automated TestingAndroid Automated Testing
Android Automated Testingroisagiv
 

Similar to The Gradle in Ratpack: Dissected (20)

High Performance Microservices with Ratpack and Spring Boot
High Performance Microservices with Ratpack and Spring BootHigh Performance Microservices with Ratpack and Spring Boot
High Performance Microservices with Ratpack and Spring Boot
 
Construire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradleConstruire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradle
 
TDC2018SP | Trilha Go - Processando analise genetica em background com Go
TDC2018SP | Trilha Go - Processando analise genetica em background com GoTDC2018SP | Trilha Go - Processando analise genetica em background com Go
TDC2018SP | Trilha Go - Processando analise genetica em background com Go
 
Node.js - async for the rest of us.
Node.js - async for the rest of us.Node.js - async for the rest of us.
Node.js - async for the rest of us.
 
Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01
 
JS everywhere 2011
JS everywhere 2011JS everywhere 2011
JS everywhere 2011
 
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
 
Soft-Shake 2016 : Jigsaw est prêt à tuer le classpath
Soft-Shake 2016 : Jigsaw  est prêt à tuer le classpathSoft-Shake 2016 : Jigsaw  est prêt à tuer le classpath
Soft-Shake 2016 : Jigsaw est prêt à tuer le classpath
 
Groovy Ecosystem - JFokus 2011 - Guillaume Laforge
Groovy Ecosystem - JFokus 2011 - Guillaume LaforgeGroovy Ecosystem - JFokus 2011 - Guillaume Laforge
Groovy Ecosystem - JFokus 2011 - Guillaume Laforge
 
Jenkins and Groovy
Jenkins and GroovyJenkins and Groovy
Jenkins and Groovy
 
Puppet Module Reusability - What I Learned from Shipping to the Forge
Puppet Module Reusability - What I Learned from Shipping to the ForgePuppet Module Reusability - What I Learned from Shipping to the Forge
Puppet Module Reusability - What I Learned from Shipping to the Forge
 
OSGi ecosystems compared on Apache Karaf - Christian Schneider
OSGi ecosystems compared on Apache Karaf - Christian SchneiderOSGi ecosystems compared on Apache Karaf - Christian Schneider
OSGi ecosystems compared on Apache Karaf - Christian Schneider
 
Dropwizard and Friends
Dropwizard and FriendsDropwizard and Friends
Dropwizard and Friends
 
Grunt & Front-end Workflow
Grunt & Front-end WorkflowGrunt & Front-end Workflow
Grunt & Front-end Workflow
 
こわくないよ❤️ Playframeworkソースコードリーディング入門
こわくないよ❤️ Playframeworkソースコードリーディング入門こわくないよ❤️ Playframeworkソースコードリーディング入門
こわくないよ❤️ Playframeworkソースコードリーディング入門
 
Dart Workshop
Dart WorkshopDart Workshop
Dart Workshop
 
Oredev 2015 - Taming Java Agents
Oredev 2015 - Taming Java AgentsOredev 2015 - Taming Java Agents
Oredev 2015 - Taming Java Agents
 
GradleFX
GradleFXGradleFX
GradleFX
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing Up
 
Android Automated Testing
Android Automated TestingAndroid Automated Testing
Android Automated Testing
 

Recently uploaded

presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyKhushali Kathiriya
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024The Digital Insurer
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Jeffrey Haguewood
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbuapidays
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...apidays
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodJuan lago vázquez
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native ApplicationsWSO2
 
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot ModelNavi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot ModelDeepika Singh
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdflior mazor
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024The Digital Insurer
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWERMadyBayot
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 

Recently uploaded (20)

presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot ModelNavi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 

The Gradle in Ratpack: Dissected

  • 1. The Gradle in Ratpack: Dissected Presented by David Carr Gradle Summit 2016 - June 22 GitHub: davidmc24 Twitter: @varzof
  • 2. Who Am I? • Systems Architect at CommerceHub • Java/Groovy Developer • Gradle Enthusiast • Ratpack Contributor
  • 3. What is Ratpack? • A web framework • A set of Java libraries • Purely a runtime • No installable package • No coupled/required build tooling • Gradle recommended; plugins provided
  • 4. Ratpack Hello World Using the ratpack-groovy Gradle plugin
  • 5. build.gradle plugins { id "io.ratpack.ratpack-groovy" version "1.3.3" } repositories { jcenter() } dependencies { runtime "org.slf4j:slf4j-simple:1.7.21" }
  • 7. gradle run $ gradle run :compileJava UP-TO-DATE :compileGroovy UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :configureRun :run [main] INFO ratpack.server.RatpackServer - Starting server... [main] INFO ratpack.server.RatpackServer - Building registry... [main] INFO ratpack.server.RatpackServer - Ratpack started (development) for http://localhost:5050
  • 8. gradle run --continuous $ gradle run --continuous --quiet [main] INFO ratpack.server.RatpackServer - Starting server... [main] INFO ratpack.server.RatpackServer - Building registry... [main] INFO ratpack.server.RatpackServer - Ratpack started (development) for http://localhost:5050 [main] INFO ratpack.server.RatpackServer - Stopping server... [main] INFO ratpack.server.RatpackServer - Server stopped. [main] INFO ratpack.server.RatpackServer - Starting server... [main] INFO ratpack.server.RatpackServer - Building registry... [main] INFO ratpack.server.RatpackServer - Ratpack started (development) for http://localhost:5050 $
  • 10. RatpackBasePlugin class RatpackBasePlugin implements Plugin<Project> { @Override void apply(Project project) { project.extensions.create( "ratpack", RatpackExtension, project) } }
  • 11. RatpackPlugin private static final GradleVersion GRADLE_VERSION_BASELINE = GradleVersion.version("2.6") … void apply(Project project) { def gradleVersion = GradleVersion.version( project.gradle.gradleVersion) if (gradleVersion < GRADLE_VERSION_BASELINE) { throw new GradleException( "Ratpack requires Gradle version ${GRADLE_VERSION_BASELINE.version} or later") }
  • 12. RatpackPlugin project.plugins.apply(ApplicationPlugin) project.plugins.apply(RatpackBasePlugin) RatpackExtension ratpackExtension = project.extensions.findByType( RatpackExtension) SourceSetContainer sourceSets = project.sourceSets def mainSourceSet = sourceSets[SourceSet.MAIN_SOURCE_SET_NAME] mainSourceSet.resources.srcDir { ratpackExtension.baseDir }
  • 14. RatpackExtension class RatpackExtension { … private final DependencyHandler dependencies File baseDir RatpackExtension(Project project) { this.dependencies = project.dependencies baseDir = project.file('src/ratpack') }
  • 15. RatpackExtension class RatpackExtension { public static final String GROUP = "io.ratpack" private final version = getClass().classLoader.getResource( "ratpack/ratpack-version.txt").text.trim() … Dependency getGroovyTest() { dependency("groovy-test") } Dependency dependency(String name) { dependencies.create( "${GROUP}:ratpack-${name}:${version}") } }
  • 16. RatpackPlugin JavaExec runTask = project.tasks.findByName("run") as JavaExec def configureRun = project.task("configureRun") configureRun.doFirst { runTask.with { systemProperty "ratpack.development", true } } runTask.dependsOn configureRun
  • 17. RatpackGroovyPlugin class RatpackGroovyPlugin implements Plugin<Project> { @Override void apply(Project project) { project.plugins.apply(RatpackPlugin) project.plugins.apply(GroovyPlugin) project.mainClassName = "ratpack.groovy.GroovyRatpackMain" …
  • 18. RatpackGroovyPlugin … def ratpackExtension = project.extensions .getByType(RatpackExtension) project.dependencies { compile ratpackExtension.groovy testCompile ratpackExtension.groovyTest } } }
  • 19. Plugin Testing Using Spock and Gradle TestKit
  • 20. DslSpec class DslSpec extends FunctionalSpec { def "can use extension to add dependencies"() { given: buildFile << """ configurations.all { transitive = false // we don't need jackson itself for this test } dependencies { compile ratpack.dependency("jackson-guice") } task showDeps { doLast { file("deps.txt") << configurations.compile.dependencies*.toString().join('n') } } """ when: run "showDeps" then: file("deps.txt").text.contains("ratpack-jackson-guice") } }
  • 21. InstallDistSpec def "everything goes in the right place"() { given: file("src/ratpack/ratpack.groovy") << """ import static ratpack.groovy.Groovy.* import ratpack.server.Stopper ratpack { serverConfig { port 0 } handlers { get { onClose { get(Stopper).stop() } render "foo" } get("stop") { onClose { get(ratpack.server.Stopper).stop() } } } } """
  • 22. InstallDistSpec when: run "installDist" def process = new ProcessBuilder() .directory(file("build/install/test-app")) .command(osSpecificCommand()) .start() def port = scrapePort(process) then: new PollingConditions().within(10) { try { urlText(port) == "foo" } catch (ConnectException ignore) { false } } cleanup: if (port) { url(port, "stop") } process?.destroy() process?.waitFor() }
  • 23. FunctionalSpec abstract class FunctionalSpec extends Specification { GradleRunner runner(String... args) { GradleRunner.create() .withProjectDir(dir.root) .withDebug(true) .forwardOutput() .withTestKitDir(getTestKitDir()) .withArguments(args.toList()) } BuildResult run(String... args) { runner(args).build() } BuildResult fail(String... args) { runner(args).buildAndFail() }
  • 24. FunctionalSpec File getBuildFile() { makeFile("build.gradle") } File makeFile(String path) { def f = file(path) if (!f.exists()) { def parts = path.split("/") if (parts.size() > 1) { dir.newFolder(*parts[0..-2]) } dir.newFile(path) } f } File file(String path) { def file = new File(dir.root, path) assert file.parentFile.mkdirs() || file.parentFile.exists() file }
  • 25. FunctionalSpec def setup() { file("settings.gradle") << "rootProject.name = 'test-app'" buildFile << """ buildscript { repositories { maven { url "${localRepo.toURI()}" } jcenter() } repositories { jcenter() } dependencies { classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3' classpath 'io.ratpack:ratpack-gradle:${RATPACK_VERSION}' } } apply plugin: 'io.ratpack.ratpack-groovy' apply plugin: 'com.github.johnrengelman.shadow' version = "1.0" repositories { maven { url "${localRepo.toURI()}" } jcenter() } dependencies { compile 'org.slf4j:slf4j-simple:1.7.12' } """ file("src/ratpack/ratpack.properties") << "port=0n" }
  • 26. FunctionalSpec int scrapePort(Process process) { int port = -1 def latch = new CountDownLatch(1) Thread.start { process.errorStream.eachLine { String line -> System.err.println(line) if (latch.count) { if (line.contains("Ratpack started for http://localhost:")) { def matcher = (line =~ "http://localhost:(d+)") port = matcher[0][1].toString().toInteger() latch.countDown() } } } } if (!latch.await(15, TimeUnit.SECONDS)) { throw new RuntimeException("Timeout waiting for application to start") } port }
  • 28. Build File Naming def setBuildFile(project) { project.buildFileName = "${project.name}.gradle" project.children.each { setBuildFile(it) } } setBuildFile(rootProject)
  • 29. Build Logic Re-use Usage: apply from: "$rootDir/gradle/javaModule.gradle" Extracted build files: checkstyle, codenarc, dependencies, dependencyRules, groovyModule, javaModule, markdown2book, pom, projectLocalRepo, publish, ratpackLocal, versionFile
  • 30. Version as a Resource task writeVersionNumberFile { def versionFile = file("src/main/resources/ratpack/ratpack-version.txt") inputs.property "version", project.version outputs.file versionFile doFirst { assert versionFile.parentFile.mkdirs() || versionFile.parentFile.directory versionFile.text = project.version } } processResources.dependsOn writeVersionNumberFile plugins.withType(EclipsePlugin) { eclipseProject.dependsOn writeVersionNumberFile } plugins.withType(IdeaPlugin) { ideaModule.dependsOn writeVersionNumberFile }
  • 31. Minimum Groovy Version processResources { inputs.property("groovyVersion", commonVersions.groovy) def minimumGroovyVersionFile = file("src/main/resources/ratpack/ minimum-groovy-version.txt") outputs.file minimumGroovyVersionFile doFirst { minimumGroovyVersionFile.parentFile.mkdirs() minimumGroovyVersionFile.text = commonVersions.groovy } }
  • 32. Java Settings def jvmEncoding = java.nio.charset.Charset.defaultCharset().name() if (jvmEncoding != "UTF-8") { throw new IllegalStateException( "Build environment must be UTF-8 (it is: $jvmEncoding) - add '-Dfile.encoding=UTF-8' to the GRADLE_OPTS environment variable ") } if (!JavaVersion.current().java8Compatible) { throw new IllegalStateException( "Must be built with Java 8 or higher") }
  • 33. Download Dependencies task downloadDependencies << { allprojects { p -> configurations.each { c -> println "Downloading dependencies for $p.path - $c.name" c.files } } }
  • 34. Version Management ext { commonVersions = [ netty: "4.1.1.Final", slf4j: "1.7.12", spock: "1.0-groovy-2.4", ] commonDependencies = [ slf4j: "org.slf4j:slf4j-api:${commonVersions.slf4j}", spock: dependencies.create( "org.spockframework:spock-core:${commonVersions.spock}", { exclude group: "org.codehaus.groovy", module: "groovy-all" }), ] … compile commonDependencies.slf4j compile "io.netty:netty-codec-http:$commonVersions.netty" compile "io.netty:netty-handler:$commonVersions.netty"
  • 35. Alternate Groovy Version if (System.getenv('CI_GROOVY_VERSION')) { commonVersions.groovy = System.getenv('CI_GROOVY_VERSION') if (commonVersions.groovy.endsWith('-SNAPSHOT')) { allprojects { repositories { maven { name 'JFrog OSS snapshot repo' url 'https://oss.jfrog.org/oss-snapshot-local/' } } } } }
  • 36. Cloud CI Detection ext { isTravis = System.getenv("TRAVIS") != null isDrone = System.getenv("DRONE") != null isCodeship = System.getenv("CI_NAME") == "codeship" isSnapCi = System.getenv("SNAP_CI") != null isAppveyor = System.getenv("APPVEYOR") != null isHeroku = System.getenv("HEROKU") != null isCloudCi = isTravis || isDrone || isCodeship || isSnapCi || isAppveyor isCi = isCloudCi }
  • 37. Memory Usage test { maxParallelForks = 3 } tasks.withType(GroovyCompile) { groovyOptions.forkOptions.memoryMaximumSize = "512m" } tasks.withType(Test) { jvmArgs "-Xss320k" minHeapSize "120m" maxHeapSize "280m" doFirst { if (isCloudCi) { maxParallelForks 1 systemProperty "cloudCi", "true" } } }
  • 38. Command-line Options In Snap CI: GRADLE_OPTS= -Xmx386m -Xms386m -Dorg.gradle.parallel.intra=true ./gradlew check --parallel --max-workers=2 --gradle-user-home ${SNAP_CACHE_DIR}/.gradle
  • 39. Questions? P.S. We're Hiring! David M. Carr Systems Architect GitHub: davidmc24 Twitter: @varzof

Editor's Notes

  1. Good morning, everyone. Today we're going to be examining the usage of Gradle in the Ratpack open source project. If you have any questions, feel free to to ask throughout the talk.
  2. Before we get started, let's cover some background. I'm David Carr. I'm a Systems Architect at CommerceHub, where I've been working with Java and Groovy for many years now. I'm also a contributor to the Ratpack project.
  3. So, what *is* Ratpack exactly? In terms of its basic function, it serves as a web framework. Unlike some other frameworks, however, it operates solely as a set of Java libraries you compile and run against. You don't "install" ratpack, you just build an app using it, just like you would any other library. Due to this, the Ratpack project itself is a great reference example of a non-trivial multi-project Gradle build. Similarly, Ratpack's Gradle plugin is a useful example of Gradle plugin techniques.
  4. So, what does a minimal Ratpack project using the ratpack-groovy plugin look like?
  5. Here's an example build.gradle file. First we declare that we want to use the plugin. Then, we tell it where to resolve artifacts from. Then, we give it a logging library... not because it's strictly necessary, but but because I hate "no SLF4J binding found" warnings. If we run it now, it will tell us "No ratpack.groovy found"... so let's create one.
  6. For a Groovy-based Ratpack application, this is the entry point for the application logic. In this case, we keep it really simple... respond to all requests with "Hello world". Great! Let's run it!
  7. So, we run "gradle run"... and we have a running app.
  8. That's great and all... but we've got apps to write. We can't be spending all day restarting builds to get test updates. So let's have it automatically build and restart for us! If we run it with the --continuous option, when we change a source file, it will automatically re-build the application and re-start the app.
  9. You may have noticed that there are several things you'd usually need that were missing from this process. There weren't any dependency declarations for Groovy, or Ratpack itself, nor a specification for how to run the application. Those are all configured for us by the plugin. Let's start taking a look at how that works.
  10. It's a general good practice to separate capability-based plugins from convention-based plugins. Here, you see Ratpack's capability-based plugin, which just installs an extension. We'll take a look at the body of the extension in a moment.
  11. The Ratpack plugin builds on top of the base plugin with conventions. First we ensure that the Gradle version is high enough.
  12. Then, we apply plugins used by the remainder of the plugin. Next, we add the src/ratpack directory as a resource directory in the main source set.
  13. After that, we add some dependencies to the project, so that the user doesn't need to in their build.gradle. For Java projects, we add the Ratpack core and test modules. Note that we're accessing methods on the extension object.
  14. The extension keeps track of the src/ratpack directory (which we saw used in the RatpackPlugin), as well as retaining a reference to the project's dependency handler.
  15. It uses the dependency handler to provide utility methods such as "core", and "test", as well as support for arbitrary dependencies. All of them resolve to creating a dependency for the specified Ratpack module at the same version as the Ratpack plugin.
  16. Back to the Ratpack plugin, we tweak the "run" task provided by the Application plugin, and specify an additional system property. This signals the application to run in development mode. There's also some logic in here to tie into Gradle's continuous build lifecycle, but I'm not going to cover that in detail as it's currently still incubating, and we're using internal APIs to get it to work the way we want.
  17. In the Ratpack Groovy plugin, we leverage all of the functionality configured in the Ratpack plugin, plus we apply the Gradle core Groovy plugin, and add a default entry point for the application, adding support for the ratpack.groovy script file we saw in the example app.
  18. We also add some additional dependencies; the ratpack groovy library and the ratpack groovy test library.
  19. Ratpack's plugin testing is based around Spock and the Gradle TestKit. TestKit is currently focussed on functional testing.
  20. All of the logic for interacting with TestKit to interact with Gradle is isolated in a FunctionalSpec abstract class, while the test specs focus on the contents of build files, running tasks, and the expected results. This test just verifies that the dependency declaration functionality on the extension works as expected.
  21. Here's a test that builds and starts a Ratpack app. First, we create the Ratpack groovy script.
  22. Then, we run the build to install it, run the resulting start script, and figure out what port it ran on. Once it's up, we assert on the expected URL's contents, and then clean up the test.
  23. Here, we have the base class. It defines utility methods to wrap the TestKit, running Gradle tasks with either expected success or failure.
  24. We have some utility methods to easily create files within the test's project directory.
  25. In the setup method, we set up a basic build file, which individual tests can append to. The ratpack version is loaded from a private repository on the local disk, since it's likely that you'll be running the tests with a local snapshot version.
  26. To scrape the port of running processes, we start a thread to consume stderr, and return the parsed port. If the expected expression isn't matched within a timeout, we consider it a test failure.
  27. Now that we've examined the Ratpack Gradle plugins, let's move on to the Ratpack project's own build.
  28. The Ratpack project is currently composed of 28 separate modules. Some are the main Ratpack libraries, while others are integrations with third-party libraries, and others are used for project-internal purposes, such as performance testing the project, generating the Ratpack documentation and running the project website. When you have that many build files, having them all be "build.gradle" gets in the way. Instead, we use this snippet in settings.gradle to have the build script for each sub-project named after the project. Thus, we have a ratpack-core.gradle and a ratpack-guice.gradle. In this way, we can easily use IntelliJ's "Navigate to File" support to jump right to the desired build script.
  29. As your project gets bigger, it gets more difficult to manage your build logic. Sometimes it can be useful to separate out concepts into their own build script file, so that you can easily find that portion of the build logic. In a multi-project build, you may also have re-use to consider, as multiple sub-projects may require the same logic. The approach used in the Ratpack project is to have a "gradle" directory in the root of the project, and store extracted build scripts there. These can then be used as needed with an "apply from" statement.
  30. The Ratpack Gradle plugins need to know what version of Ratpack to use at build time, and Ratpack Core exposes the current version at runtime. Both of these could be accomplished by hard-coding the version in a class, or manually updating a resource file, but those approaches aren't particularly elegant, and are prone to humans forgetting to update them. Instead, we introduce a Gradle task that writes a version file resource automatically based on the version of the Gradle project. By properly declaring the inputs and outputs, we ensure that Gradle's incremental build support works as expected when the project version changes. We want the file to be present regardless of whether you're building Ratpack via Gradle or in an IDE, so we have a few tasks depend on the writeVersionNumberFile task.
  31. Similar to the version number file, we need a resource available at runtime with the version of Groovy expected for Ratpack, so that we can verify that the version on the classpath is modern enough to be supported. Here the basic technique is the same, but instead of adding a new task, we add a new action to the existing processResources task.
  32. It can be tricky to ensure a consistent build environment. This is especially the case in open source projects, where contributors may have very diverse hardware and software configurations. When possible, it's desirable to automate detection and enforcement of any known requirements in the build itself. We use the Gradle wrapper to control the version and installation of Gradle. For the file encoding and Java version used at build-time, we check them in the build, and fail if they aren't acceptable. By failing early with a clear message, we avoid harder to diagnose errors later on in the build process.
  33. Sometimes, it may be useful to work offline, such as in an airplane. Gradle has a "--offline" option that tells Gradle to load dependencies from its cache rather than checking for updates over the network. However, that only works if you have a cached version of the artifacts before you go offline. You could run the tasks that you use to build, test, etc. to ensure that the required artifacts are present, but that can take a while, and only caches the artifacts for the configurations you happen to use in those tasks. This approach allows easily downloading the artifacts for all potential tasks, as long as they use Gradle configurations for dependency resolution.
  34. In the Ratpack build, there are some versions that need to be referenced multiple times. For example, the version of SLF4J is used as a compile dependency in ratpack-core and as a token when assembling the documentation. It's also used to load compatible versions of SLF4J bridge and backend libraries in several modules. We declare the version as an extension property, which can then be accessed elsewhere in the build. In addition to version numbers being re-used, sometimes specific dependencies are re-used. In addition to reducing redundancy and giving a single point to update if the artifact ID changes, this also allows for centralizing the declaration of transitive dependency exclusions or dependencies that should always be used together.
  35. Another advantage of having a central declaration of dependencies is that it provides a central place to update. In this case, the Groovy team runs a CI server to check the compatibility of new versions of Groovy with widely used community projects. When that's happening, we want to run against the specified version of Groovy, rather than the one we normally would use. If the specified version is a snapshot, we add an additional repository for dependency resolution, as the repositories we normally use don't store snapshots.
  36. Ratpack currently uses SnapCI for our continuous integration builds. However, that wasn't always the case. When I started working on the project, we were on Travis CI, and we tried several others after that. The Ratpack build leverages parallelism when available to improve performance. On my laptop, a clean build takes around 5 minutes, using all 4 cores. However, due to the parallelism, it can easily hit memory or CPU limitations on cloud CI providers. In order to handle both cases, powerful developer machines and resource-constrained CI environments, we first need to detect which situation currently applies. We do this by checking environment variables set by cloud CI providers, and storing extension properties based on the analysis.
  37. Then, we're free to tweak settings based on them. In some cases, we apply the change all the time; in others, we only apply it when running in CI. To control memory usage, we've explicitly stated heap sizes for most tasks that fork a JVM. In addition, we explicitly reduced the stack trace size when running tests, as running larger numbers of threads with large stack traces can bloat memory usage. In the ratpack build, there's actually 3 levels of parallelism potentially at play. The Gradle Test task allows for multiple tests to run concurrently. Here, we've set it to run up to three test processes at a time... but restrict it to only one at a time when running in CI.
  38. Second, there's project parallelism, which is an incubating feature. That's enabled via the --parallel option to Gradle on the command-line, and is only applicable to multi-project builds. Effectively, if projects don't have any dependencies on each other, it can run them in parallel with each other, restricted by the --max-workers setting, which defaults to the number of available processors. The third form of parallelism is intra-project parallelism. This is currently an incubating, undocumented capability. Here, we enable it by setting an undocumented system property. You can confirm that it's working by running the build with --info; a different message will be logged if you're using intra-project parallelism. In this mode, it will run tasks within a project in parallel with each other.
  39. So... that's all I've got for today. I hope you found at least something informative or interesting. I think we have time for a couple questions. If there's something we don't get to, I'll be around for the rest of the conference; feel free to come up to me to chat.