the gradle in ratpack: dissected

39
The Gradle in Ratpack: Dissected Presented by David Carr Gradle Summit 2016 - June 22 GitHub: davidmc24 Twitter: @varzof

Upload: david-carr

Post on 25-Jan-2017

201 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: The Gradle in Ratpack: Dissected

The Gradle in Ratpack: Dissected

Presented by David CarrGradle Summit 2016 - June

22GitHub: davidmc24

Twitter: @varzof

Page 2: The Gradle in Ratpack: Dissected

Who Am I?

• Systems Architect at CommerceHub

• Java/Groovy Developer• Gradle Enthusiast• Ratpack Contributor

Page 3: The Gradle in Ratpack: Dissected

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

Page 4: The Gradle in Ratpack: Dissected

Ratpack Hello WorldUsing the ratpack-groovy Gradle

plugin

Page 5: The Gradle in Ratpack: Dissected

build.gradleplugins { id "io.ratpack.ratpack-groovy" \ version "1.3.3"}

repositories { jcenter()}

dependencies { runtime "org.slf4j:slf4j-simple:1.7.21"}

Page 6: The Gradle in Ratpack: Dissected

src/ratpack/ratpack.groovy

import static \ ratpack.groovy.Groovy.ratpack

ratpack { handlers { get { render "Hello world!\n" } }}

Page 7: The Gradle in Ratpack: Dissected

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

Page 8: The Gradle in Ratpack: Dissected

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$

Page 9: The Gradle in Ratpack: Dissected

Ratpack Gradle PluginsBase, Java, Groovy

Page 10: The Gradle in Ratpack: Dissected

RatpackBasePluginclass RatpackBasePlugin implements Plugin<Project> { @Override void apply(Project project) { project.extensions.create( "ratpack", RatpackExtension, project) }}

Page 11: The Gradle in Ratpack: Dissected

RatpackPluginprivate 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") }

Page 12: The Gradle in Ratpack: Dissected

RatpackPluginproject.plugins.apply(ApplicationPlugin)project.plugins.apply(RatpackBasePlugin)

RatpackExtension ratpackExtension = project.extensions.findByType( RatpackExtension)

SourceSetContainer sourceSets = project.sourceSetsdef mainSourceSet = sourceSets[SourceSet.MAIN_SOURCE_SET_NAME]mainSourceSet.resources.srcDir { ratpackExtension.baseDir }

Page 13: The Gradle in Ratpack: Dissected

RatpackPlugin

project.dependencies { compile ratpackExtension.core testCompile ratpackExtension.test}

Page 14: The Gradle in Ratpack: Dissected

RatpackExtensionclass RatpackExtension { … private final DependencyHandler dependencies File baseDir

RatpackExtension(Project project) { this.dependencies = project.dependencies baseDir = project.file('src/ratpack') }

Page 15: The Gradle in Ratpack: Dissected

RatpackExtensionclass 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}") }}

Page 16: The Gradle in Ratpack: Dissected

RatpackPluginJavaExec runTask = project.tasks.findByName("run") as JavaExec

def configureRun = project.task("configureRun")

configureRun.doFirst { runTask.with { systemProperty "ratpack.development", true }}runTask.dependsOn configureRun

Page 17: The Gradle in Ratpack: Dissected

RatpackGroovyPluginclass RatpackGroovyPlugin implements Plugin<Project> {

@Override void apply(Project project) { project.plugins.apply(RatpackPlugin) project.plugins.apply(GroovyPlugin) project.mainClassName = "ratpack.groovy.GroovyRatpackMain"…

Page 18: The Gradle in Ratpack: Dissected

RatpackGroovyPlugin… def ratpackExtension = project.extensions .getByType(RatpackExtension) project.dependencies { compile ratpackExtension.groovy testCompile ratpackExtension.groovyTest } }}

Page 19: The Gradle in Ratpack: Dissected

Plugin TestingUsing Spock and Gradle TestKit

Page 20: The Gradle in Ratpack: Dissected

DslSpecclass 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") }}

Page 21: The Gradle in Ratpack: Dissected

InstallDistSpecdef "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() } } } } """

Page 22: The Gradle in Ratpack: Dissected

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()}

Page 23: The Gradle in Ratpack: Dissected

FunctionalSpecabstract 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() }

Page 24: The Gradle in Ratpack: Dissected

FunctionalSpecFile 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}

Page 25: The Gradle in Ratpack: Dissected

FunctionalSpecdef 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=0\n"}

Page 26: The Gradle in Ratpack: Dissected

FunctionalSpecint 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}

Page 27: The Gradle in Ratpack: Dissected

Ratpack Project Build

Page 28: The Gradle in Ratpack: Dissected

Build File Namingdef setBuildFile(project) { project.buildFileName = "${project.name}.gradle" project.children.each { setBuildFile(it) }}

setBuildFile(rootProject)

Page 29: The Gradle in Ratpack: Dissected

Build Logic Re-useUsage:

apply from: \"$rootDir/gradle/javaModule.gradle"

Extracted build files:

checkstyle, codenarc, dependencies, dependencyRules, groovyModule, javaModule, markdown2book, pom, projectLocalRepo, publish, ratpackLocal, versionFile

Page 30: The Gradle in Ratpack: Dissected

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}

Page 31: The Gradle in Ratpack: Dissected

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 }}

Page 32: The Gradle in Ratpack: Dissected

Java Settingsdef 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")}

Page 33: The Gradle in Ratpack: Dissected

Download Dependencies

task downloadDependencies << { allprojects { p -> configurations.each { c -> println "Downloading dependencies for $p.path - $c.name" c.files } }}

Page 34: The Gradle in Ratpack: Dissected

Version Managementext { 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.slf4jcompile "io.netty:netty-codec-http:$commonVersions.netty"compile "io.netty:netty-handler:$commonVersions.netty"

Page 35: The Gradle in Ratpack: Dissected

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/' } } } }}

Page 36: The Gradle in Ratpack: Dissected

Cloud CI Detectionext { 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}

Page 37: The Gradle in Ratpack: Dissected

Memory Usagetest { 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" } }}

Page 38: The Gradle in Ratpack: Dissected

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

Page 39: The Gradle in Ratpack: Dissected

Questions?P.S. We're Hiring!

David M. CarrSystems ArchitectGitHub: davidmc24

Twitter: @varzof