You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by ad...@apache.org on 2017/08/03 21:09:54 UTC

[2/2] kudu git commit: KUDU-2066. Add experimental Gradle build support

KUDU-2066. Add experimental Gradle build support

Adds an experimental Gradle build that results in similar
tasks and artifacts as the existing maven build. See the
readme for usage and common commands.

The build is broken out by module with common configurations
in the root build.gradle file. Additionally the gradle
directory contains “drop in” scripts containing functional
pieces of the build configuration to simplify sharing
configurations and the unclutter the main build files.

Includes support for:
- Shaded dependencies that can be used across modules
- Running unit tests before integration tests
- Reporting available dependency updates
- Protobuf and Avro code generation
- Publishing to remote and local maven repositories
- Cross compiling scala
- Code quality checks (checkstyle, findbugs, pmd)

Change-Id: Ib257cdd019a1f383c886b9238bb47d96576c4421
Reviewed-on: http://gerrit.cloudera.org:8080/7258
Reviewed-by: Adar Dembo <ad...@cloudera.com>
Tested-by: Kudu Jenkins


Project: http://git-wip-us.apache.org/repos/asf/kudu/repo
Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/e5202d30
Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/e5202d30
Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/e5202d30

Branch: refs/heads/master
Commit: e5202d30a6ab89ec800931030728ed7827a137da
Parents: de8b92e
Author: Grant Henke <gr...@gmail.com>
Authored: Mon Jun 19 09:35:55 2017 -0500
Committer: Adar Dembo <ad...@cloudera.com>
Committed: Thu Aug 3 21:09:15 2017 +0000

----------------------------------------------------------------------
 build-support/release/rat_exclude_files.txt     |   3 +
 java/.gitignore                                 |   6 +
 java/README.md                                  |  55 +++++-
 java/build.gradle                               |  48 +++++
 java/gradle.properties                          |  41 +++++
 java/gradle/artifacts.gradle                    |  57 ++++++
 java/gradle/buildscript.gradle                  |  37 ++++
 java/gradle/compile.gradle                      |  37 ++++
 java/gradle/dependencies.gradle                 | 105 +++++++++++
 java/gradle/properties.gradle                   |  53 ++++++
 java/gradle/protobuf.gradle                     |  34 ++++
 java/gradle/publishing.gradle                   |  62 +++++++
 java/gradle/quality.gradle                      |  85 +++++++++
 java/gradle/scopes.gradle                       |  23 +++
 java/gradle/shadow.gradle                       | 110 ++++++++++++
 java/gradle/tests.gradle                        |  80 +++++++++
 java/gradle/wrapper.gradle                      |  64 +++++++
 java/gradle/wrapper/gradle-wrapper.properties   |   6 +
 java/gradlew                                    | 177 +++++++++++++++++++
 java/kudu-client-tools/build.gradle             |  32 ++++
 java/kudu-client/build.gradle                   |  70 ++++++++
 java/kudu-flume-sink/build.gradle               |  33 ++++
 .../flume/sink/AvroKuduOperationsProducer.java  |   2 +-
 .../org/apache/kudu/flume/sink/KuduSink.java    |   2 +-
 .../sink/SimpleKeyedKuduOperationsProducer.java |   2 +-
 .../sink/SimpleKuduOperationsProducer.java      |   2 +-
 java/kudu-jepsen/build.gradle                   |  66 +++++++
 java/kudu-mapreduce/build.gradle                |  34 ++++
 java/kudu-spark-tools/build.gradle              |  37 ++++
 java/kudu-spark-tools/pom.xml                   |   6 +
 .../kudu/spark/tools/ITBigLinkedListTest.scala  |  84 +++++++++
 .../IntegrationTestBigLinkedListTest.scala      |  84 ---------
 java/kudu-spark/build.gradle                    |  54 ++++++
 java/kudu-spark/pom.xml                         |   6 +
 java/settings.gradle                            |  28 +++
 35 files changed, 1536 insertions(+), 89 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/build-support/release/rat_exclude_files.txt
----------------------------------------------------------------------
diff --git a/build-support/release/rat_exclude_files.txt b/build-support/release/rat_exclude_files.txt
index ea13868..298f28c 100644
--- a/build-support/release/rat_exclude_files.txt
+++ b/build-support/release/rat_exclude_files.txt
@@ -10,6 +10,9 @@ pax_global_header
 version.txt
 build-support/release/rat_exclude_files.txt
 docs/support/doxygen/client_api.footer.in
+java/gradlew
+java/gradle/gradle-wrapper.jar
+java/gradle/gradle-wrapper.properties
 java/kudu-flume-sink/src/test/avro/testAvroKuduOperationsProducer.avsc
 java/kudu-client/src/main/java/com/google/protobuf/ZeroCopyLiteralByteString.java
 java/kudu-client/src/main/java/org/apache/kudu/client/AsyncKuduClient.java

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/.gitignore
----------------------------------------------------------------------
diff --git a/java/.gitignore b/java/.gitignore
index fd62d76..b418b4c 100644
--- a/java/.gitignore
+++ b/java/.gitignore
@@ -16,6 +16,7 @@
 # under the License.
 
 # Eclipse files
+.metadata/
 .classpath
 .project
 .settings/
@@ -24,8 +25,13 @@
 target/
 dependency-reduced-pom.xml
 
+# Gradle build artifacts
+.gradle
+build
+
 # IntelliJ
 *.ipr
 *.iws
 *.iml
 .idea/
+classes

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/README.md
----------------------------------------------------------------------
diff --git a/java/README.md b/java/README.md
index 8bd8b85..3b65f27 100644
--- a/java/README.md
+++ b/java/README.md
@@ -25,7 +25,7 @@ Building the Client
 
 $ mvn package -DskipTests
 
-The client jar will can then be found at kudu-client/target.
+The client jar can then be found at kudu-client/target.
 
 Running the Tests
 ------------------------------------------------------------
@@ -135,3 +135,56 @@ likely a bug in maven-protoc-plugin.
 
 There's a simple workaround: delete the errant folder within
 Eclipse and refresh the kudu-client project.
+
+Building with Gradle
+--------------------
+
+As an experiment a Gradle build definition also exists.
+In order to run the Gradle build you must install [Gradle|https://gradle.org/].
+If you would rather not install Gradle locally, you can use the
+[Gradle Wrapper|https://docs.gradle.org/current/userguide/gradle_wrapper.html]
+by replacing all references to gradle with gradlew.
+
+## Running a full build
+
+This will build all modules and run all "checks".
+
+$ gradle buildAll
+
+## Building the Client
+$ gradle :kudu-client:assemble
+
+The client jar can then be found at kudu-client/build/libs.
+
+## Running the Tests
+$ gradle test
+
+Integration tests, including tests which cover Hadoop integration,
+may be run with:
+
+$ gradle integrationTest
+
+*Note:* Integration tests may depend on built Kudu binaries.
+
+## Building the Kudu-Spark integration
+
+Builds with Spark 2.x with Scala 2.11 (the default) or
+Spark 1.x with Scala 2.10.
+
+$ gradle :kudu-spark:assemble
+$ gradle :kudu-spark:assemble -PscalaVersions=2.10.6
+
+## Installing to local maven repo
+
+$ gradle install
+
+## Clearing cached build state
+
+$ gradle clean
+
+## Discovering other tasks
+
+$ gradle tasks
+
+
+

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/build.gradle
----------------------------------------------------------------------
diff --git a/java/build.gradle b/java/build.gradle
new file mode 100755
index 0000000..6bd21fc
--- /dev/null
+++ b/java/build.gradle
@@ -0,0 +1,48 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file is the entry-point for the gradle build and contains
+// common logic for the various subprojects in the build.
+
+// Load the buildscript file to apply dependencies needed for the gradle build itself.
+buildscript { apply from: file("gradle/buildscript.gradle"), to: buildscript }
+
+// Plugins and scripts applied at the root level only, instead of per module.
+apply plugin: "idea"
+apply plugin: "eclipse"
+apply from: "$rootDir/gradle/properties.gradle"
+apply from: "$rootDir/gradle/dependencies.gradle"
+apply from: "$rootDir/gradle/wrapper.gradle"
+
+subprojects {
+  // Plugins and scripts are applied in the natural "build order"
+  // they are used to ensure there are no dependency issues.
+  // These are common to all subprojects. However, subprojects may
+  // include their own plugins and scripts as well.
+  apply plugin: "java"
+  apply from: "$rootDir/gradle/scopes.gradle"
+  apply from: "$rootDir/gradle/compile.gradle"
+  apply from: "$rootDir/gradle/tests.gradle"
+  apply from: "$rootDir/gradle/quality.gradle"
+  apply from: "$rootDir/gradle/artifacts.gradle"
+  apply from: "$rootDir/gradle/publishing.gradle"
+
+  repositories {
+    mavenCentral()
+    mavenLocal()
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradle.properties
----------------------------------------------------------------------
diff --git a/java/gradle.properties b/java/gradle.properties
new file mode 100755
index 0000000..2ad95e7
--- /dev/null
+++ b/java/gradle.properties
@@ -0,0 +1,41 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# This file contains project properties.
+# More about how to use the gradle.properties file can be read here:
+#   https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties
+
+group = org.apache.kudu
+version = 1.5.0-SNAPSHOT
+url = https://kudu.apache.org/
+scmUrl = git://git.apache.org/kudu.git
+issueTrackerUrl = https://issues.apache.org/jira/projects/KUDU
+
+javaSourceCompatibility = 1.7
+encoding = UTF-8
+
+# Maximum parallel forks to use while unit testing.
+maxParallelForks = 1
+
+# Flags to speed up the gradle build.
+# https://docs.gradle.org/current/userguide/build_environment.html
+org.gradle.daemon=true
+# The below configurations are experimental but a nice performance boost.
+# org.gradle.caching=true
+# org.gradle.configureondemand=true
+# org.gradle.parallel=true
+# org.gradle.workers.max=4
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradle/artifacts.gradle
----------------------------------------------------------------------
diff --git a/java/gradle/artifacts.gradle b/java/gradle/artifacts.gradle
new file mode 100755
index 0000000..69a74ab
--- /dev/null
+++ b/java/gradle/artifacts.gradle
@@ -0,0 +1,57 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file contains common tasks and configuration for artifact generation.
+
+// Create a configuration so that the test jar can be referenced in other modules.
+configurations.create("test")
+
+task testJar(type: Jar, dependsOn: testClasses, group: "Build") {
+  description = "Assembles a jar archive containing the test classes."
+  from sourceSets.test.output
+  classifier = "tests"
+  extension "jar"
+}
+
+task sourcesJar(type: Jar, dependsOn: classes, group: "Build") {
+  description = "Assembles a jar archive containing the main source."
+  from sourceSets.main.allSource
+  classifier "sources"
+  extension "jar"
+}
+
+task testSourcesJar(type: Jar, dependsOn: testJar, group: "Build") {
+  description = "Assembles a jar archive containing the test source."
+  from sourceSets.test.allSource
+  classifier "test-sources"
+  extension "jar"
+}
+
+task javadocJar(type: Jar, dependsOn: javadoc, group: "Build") {
+  description = "Assembles a jar archive containing the javadoc."
+  from javadoc.destinationDir
+  classifier "javadoc"
+  extension "jar"
+}
+
+artifacts {
+  test testJar
+  archives testJar
+  archives sourcesJar
+  archives testSourcesJar
+  archives javadocJar
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradle/buildscript.gradle
----------------------------------------------------------------------
diff --git a/java/gradle/buildscript.gradle b/java/gradle/buildscript.gradle
new file mode 100644
index 0000000..6f062f8
--- /dev/null
+++ b/java/gradle/buildscript.gradle
@@ -0,0 +1,37 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file contains the dependencies required for the gradle build itself.
+
+repositories {
+  mavenCentral()
+  jcenter()
+  maven { url "http://clojars.org/repo" } // Only used for the clojure plugin below.
+  maven { url "http://repo.spring.io/plugins-release" } // Only used for the propdeps plugin below.
+  maven { url "https://plugins.gradle.org/m2/" }
+}
+
+// Manage plugin dependencies since the plugin block can't be used in included build scripts yet.
+// For more details see: https://docs.gradle.org/current/userguide/plugins.html#plugins_dsl_limitations
+dependencies {
+  classpath "com.commercehub.gradle.plugin:gradle-avro-plugin:0.9.0"
+  classpath "com.github.ben-manes:gradle-versions-plugin:0.15.0"
+  classpath "com.github.jengelman.gradle.plugins:shadow:2.0.1"
+  classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.1"
+  classpath "com.netflix.nebula:nebula-clojure-plugin:4.1.0"
+  classpath "io.spring.gradle:propdeps-plugin:0.0.9.RELEASE"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradle/compile.gradle
----------------------------------------------------------------------
diff --git a/java/gradle/compile.gradle b/java/gradle/compile.gradle
new file mode 100644
index 0000000..823775a
--- /dev/null
+++ b/java/gradle/compile.gradle
@@ -0,0 +1,37 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file contains common compiler configurations.
+
+// Java Configuration
+tasks.withType(JavaCompile) {
+  sourceCompatibility = javaSourceCompatibility
+  options.encoding = encoding // make sure the encoding is defined by the project and not the system default.
+  options.incremental = true // enable incremental compilation.
+}
+
+// Scala configuration
+tasks.withType(ScalaCompile) {
+  sourceCompatibility = javaSourceCompatibility
+  scalaCompileOptions.encoding = encoding // make sure the encoding is defined by the project and not the system default.
+  scalaCompileOptions.additionalParameters = [
+      // Emit warning and location for usages of features that should be imported explicitly.
+      "-feature",
+      // Emit various static analysis warnings.
+      "-Xlint"
+  ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradle/dependencies.gradle
----------------------------------------------------------------------
diff --git a/java/gradle/dependencies.gradle b/java/gradle/dependencies.gradle
new file mode 100755
index 0000000..f62015c
--- /dev/null
+++ b/java/gradle/dependencies.gradle
@@ -0,0 +1,105 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file contains all of the dependencies required for the build.
+// Centrally locating all of the dependencies ensures each subproject
+// uses the same dependency version for all dependencies used.
+
+ext {
+  versions = [:]
+  libs = [:]
+  // All supported scala versions.
+  scalaVersions = propertyWithDefault("scalaVersions", "2.11.8,2.10.6").split(",")
+}
+
+versions += [
+    async          : "1.4.1",
+    avro           : "1.8.1",
+    clojure        : "1.8.0",
+    clojureToolsCli: "0.3.5",
+    commonsIo      : "2.5",
+    findbugs       : "3.0.1",
+    flume          : "1.6.0",
+    gradle         : "4.0.2",
+    guava          : "20.0",
+    hadoop         : "2.8.1",
+    hamcrest       : "1.3",
+    jepsen         : "0.1.3",
+    jsr305         : "3.0.2",
+    junit          : "4.12",
+    log4j          : "1.2.17",
+    mockito        : "2.8.47",
+    murmur         : "1.0.0",
+    netty          : "3.10.6.Final",
+    pmd            : "5.8.1",
+    protobuf       : "3.3.0",
+    scalatest      : "3.0.3",
+    slf4j          : "1.7.25",
+    yetus          : "0.5.0"
+]
+
+// Log the Gradle version used vs defined.
+if (gradle.gradleVersion != versions.gradle) {
+  println "Using gradle version $gradle.gradleVersion (Build defines $versions.gradle)"
+}
+
+// Set the Scala version.
+// The first Scala version in the scalaVersions list is the default.
+versions["scala"] = scalaVersions.first()
+// Add base Scala version
+versions["scalaBase"] = versions.scala.substring(0, versions.scala.lastIndexOf("."))
+
+// Set the Spark version based on the Scala version.
+def spark1Version = "1.6.3"
+def spark2Version = "2.1.1"
+if ("$versions.scalaBase" == "2.10") {
+  versions["spark"] = spark1Version
+} else {
+  versions["spark"] = spark2Version
+}
+versions["sparkBase"] = versions.spark.substring(0, versions.spark.indexOf("."))
+// Log for debugging
+logger.info("Configuring Scala version: $versions.scala with Spark version: $versions.spark")
+
+libs += [
+    async             : "com.stumbleupon:async:$versions.async",
+    avro              : "org.apache.avro:avro:$versions.avro",
+    clojure           : "org.clojure:clojure:$versions.clojure",
+    clojureToolsCli   : "org.clojure:tools.cli:$versions.clojureToolsCli",
+    commonsIo         : "commons-io:commons-io:$versions.commonsIo",
+    flumeConfiguration: "org.apache.flume:flume-ng-configuration:$versions.flume",
+    flumeCore         : "org.apache.flume:flume-ng-core:$versions.flume",
+    guava             : "com.google.guava:guava:$versions.guava",
+    hadoopClient      : "org.apache.hadoop:hadoop-client:$versions.hadoop",
+    hamcrestCore      : "org.hamcrest:hamcrest-core:$versions.hamcrest",
+    jepsen            : "jepsen:jepsen:$versions.jepsen",
+    jsr305            : "com.google.code.findbugs:jsr305:$versions.jsr305",
+    junit             : "junit:junit:$versions.junit",
+    log4j             : "log4j:log4j:$versions.log4j",
+    mockitoCore       : "org.mockito:mockito-core:$versions.mockito",
+    murmur            : "com.sangupta:murmur:$versions.murmur",
+    netty             : "io.netty:netty:$versions.netty",
+    protobufJava      : "com.google.protobuf:protobuf-java:$versions.protobuf",
+    protoc            : "com.google.protobuf:protoc:$versions.protobuf",
+    scalaLibrary      : "org.scala-lang:scala-library:$versions.scala",
+    scalatest         : "org.scalatest:scalatest_$versions.scalaBase:$versions.scalatest",
+    slf4jApi          : "org.slf4j:slf4j-api:$versions.slf4j",
+    slf4jLog4j12      : "org.slf4j:slf4j-log4j12:$versions.slf4j",
+    sparkCore         : "org.apache.spark:spark-core_$versions.scalaBase:$versions.spark",
+    sparkSql          : "org.apache.spark:spark-sql_$versions.scalaBase:$versions.spark",
+    yetusAnnotations  : "org.apache.yetus:audience-annotations:$versions.yetus"
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradle/properties.gradle
----------------------------------------------------------------------
diff --git a/java/gradle/properties.gradle b/java/gradle/properties.gradle
new file mode 100644
index 0000000..1c89214
--- /dev/null
+++ b/java/gradle/properties.gradle
@@ -0,0 +1,53 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file contains methods to be used in the build to load and
+// set build properties consistently.
+
+// ext makes these methods callable project wide
+ext {
+  // A common method to handle loading gradle properties with a default when
+  // no definition is found. The property value is determined by the following
+  // priority order (top is highest priority):
+  //    - gradle property (-Pproperty=value)
+  //    - system property (-Dproperty=value)
+  //    - default value
+  // See more details on gradle property handling here:
+  //    https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_properties_and_system_properties
+  propertyWithDefault = { property, defaultValue ->
+    def value = defaultValue
+    def systemValue = System.getProperty(property)
+    if (systemValue != null) {
+      value = systemValue
+    }
+    def projectValue = project.hasProperty(property) ? project.getProperty(property) : null
+    if (projectValue != null) {
+      value = projectValue
+    }
+    logger.info("Resolved property $property with final value $value " +
+        "[defaultValue=$defaultValue, systemValue=$systemValue, projectValue=$projectValue]")
+    return value
+  }
+
+  // Returns true if the property has been set, otherwise false.
+  propertyExists = { property ->
+    if (System.getProperty(property) != null || project.hasProperty(property)) {
+      return true
+    }
+    return false
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradle/protobuf.gradle
----------------------------------------------------------------------
diff --git a/java/gradle/protobuf.gradle b/java/gradle/protobuf.gradle
new file mode 100644
index 0000000..f3d84bc
--- /dev/null
+++ b/java/gradle/protobuf.gradle
@@ -0,0 +1,34 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file contains the common protobuf plugin configuration.
+
+apply plugin: "com.google.protobuf"
+
+// Use the maven protoc artifact instead of local.
+protobuf {
+  protoc {
+    artifact = libs.protoc
+  }
+}
+
+// Configure Intellij to see the generated classes.
+idea {
+  module {
+    generatedSourceDirs += file("${protobuf.generatedFilesBaseDir}/main/java")
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradle/publishing.gradle
----------------------------------------------------------------------
diff --git a/java/gradle/publishing.gradle b/java/gradle/publishing.gradle
new file mode 100644
index 0000000..5b3ba2e
--- /dev/null
+++ b/java/gradle/publishing.gradle
@@ -0,0 +1,62 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file contains common tasks and configuration for artifact publishing.
+
+apply plugin: "maven"
+apply plugin: "signing"
+
+// Only sign artifacts if skipSigning is false,
+// the version is not a snapshot, and we are uploading them to maven.
+// This allows simplified builds and local maven installs.
+def skipSigning = propertyExists("skipSigning")
+def shouldSign = !skipSigning && !version.endsWith("SNAPSHOT") && project.gradle.startParameter.taskNames.any { it.contains("upload") }
+def mavenUrl = propertyWithDefault("mavenUrl", "")
+def mavenUsername = propertyWithDefault("mavenUsername", "")
+def mavenPassword = propertyWithDefault("mavenPassword", "")
+
+uploadArchives {
+  repositories {
+    signing {
+      required { shouldSign }
+      sign configurations.archives
+      mavenDeployer {
+        beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
+        repository(url: "${mavenUrl}") {
+          authentication(userName: "${mavenUsername}", password: "${mavenPassword}")
+        }
+      }
+    }
+  }
+}
+
+// Add the install task to the "Upload" group so it's visible in the tasks output.
+install.group = "Upload"
+
+// Sort the generated maven dependencies to make pom comparisons easier.
+tasks.withType(Upload) {
+  def installer = install.repositories.mavenInstaller
+  def deployer = uploadArchives.repositories.mavenDeployer
+
+  [installer, deployer]*.pom*.whenConfigured { pom ->
+    pom.dependencies = pom.dependencies.sort { dep ->
+      "$dep.scope:$dep.optional:$dep.groupId:$dep.artifactId"
+    }
+  }
+}
+
+

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradle/quality.gradle
----------------------------------------------------------------------
diff --git a/java/gradle/quality.gradle b/java/gradle/quality.gradle
new file mode 100644
index 0000000..1f07597
--- /dev/null
+++ b/java/gradle/quality.gradle
@@ -0,0 +1,85 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file contains common tasks and configuration for checking the quality of the code.
+
+apply plugin: "checkstyle" // Ensures Java code follows the defined coding style.
+apply plugin: "findbugs"   // Performs static code analysis to look for bugs in Java code.
+apply plugin: "pmd"        // Performs static code analysis to look for common code smells in Java code.
+apply plugin: "com.github.ben-manes.versions" // Provides a task to determine which dependencies have updates.
+
+checkstyle {
+  configFile = file("$rootDir/kudu_style.xml")
+  configProperties = [
+      "checkstyle.suppressions.file" : "$rootDir/checkstyle_suppressions.xml"
+  ]
+  ignoreFailures = true
+  showViolations = true
+}
+
+// Create an aggregate checkstyle task.
+// This simplifies running checkstyle on all the code by only needing one task instead of multiple in your command.
+task checkstyle(dependsOn: [checkstyleMain, checkstyleTest, checkstyleIntegrationTest], group: "Verification") {
+  description = "Run Checkstyle analysis."
+}
+
+findbugs {
+  toolVersion = versions.findbugs
+  ignoreFailures = true
+  effort = "max"
+}
+
+tasks.withType(FindBugs) {
+  reports {
+    xml.enabled false
+    html.enabled true
+  }
+}
+
+// Create an aggregate findbugs task.
+// This simplifies running findbugs on all the code by only needing one task instead of multiple in your command.
+task findbugs(dependsOn: [findbugsMain, findbugsTest, findbugsIntegrationTest], group: "Verification") {
+  description = "Run FindBugs analysis."
+}
+
+pmd {
+  toolVersion = versions.pmd
+  ignoreFailures = true
+}
+
+// Create an aggregate pmd task.
+// This simplifies running pmd on all the code by only needing one task instead of multiple in your command.
+task pmd(dependsOn: [pmdMain, pmdTest, pmdIntegrationTest], group: "Verification") {
+  description = "Run PMD analysis."
+}
+
+// Configure the versions plugin to only show dependency updates for released versions.
+dependencyUpdates {
+  revision = "release"
+  resolutionStrategy = {
+    componentSelection { rules ->
+      rules.all { ComponentSelection selection ->
+        boolean rejected = ["snap", "alpha", "beta", "rc", "cr", "m"].any { qualifier ->
+          selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/
+        }
+        if (rejected) {
+          selection.reject("Release candidate")
+        }
+      }
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradle/scopes.gradle
----------------------------------------------------------------------
diff --git a/java/gradle/scopes.gradle b/java/gradle/scopes.gradle
new file mode 100644
index 0000000..10bede3
--- /dev/null
+++ b/java/gradle/scopes.gradle
@@ -0,0 +1,23 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file adds provided scope and optional maven support.
+
+apply plugin: "propdeps"
+apply plugin: "propdeps-eclipse"
+apply plugin: "propdeps-idea"
+apply plugin: "propdeps-maven"

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradle/shadow.gradle
----------------------------------------------------------------------
diff --git a/java/gradle/shadow.gradle b/java/gradle/shadow.gradle
new file mode 100644
index 0000000..3663cc7
--- /dev/null
+++ b/java/gradle/shadow.gradle
@@ -0,0 +1,110 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file contains tasks and configuration to support shading dependencies
+// consistently when a subproject requires shaded artifacts.
+
+import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact
+
+apply plugin: "com.github.johnrengelman.shadow"
+
+tasks.remove(knows)  // Remove "easter egg" knows task.
+shadowJar.group = "" // Hide shadowJar task since it's used by the default build.
+
+// Configure a shaded jar to replace the default jar
+shadowJar.classifier = null // Configure shadow jar to have the default classifier.
+jar.finalizedBy(shadowJar)  // Generate the shaded jar anytime the jar task is run.
+jar.classifier = "unshaded" // Add an unshaded classifier to the default jar.
+
+// Add the shadowJar to the published artifacts.
+artifacts {
+  archives shadowJar
+}
+
+// Remove the unshaded jar from the published artifacts.
+configurations.archives.artifacts.removeAll {
+  it instanceof ArchivePublishArtifact && it.archiveTask == jar
+}
+
+// Ensure we always relocate these shaded dependencies to the same
+// location across all modules.
+shadowJar {
+  relocate "com.google.common", "org.apache.kudu.shaded.com.google.common"
+  relocate "com.google.protobuf", "org.apache.kudu.shaded.com.google.protobuf"
+  relocate "com.google.thirdparty", "org.apache.kudu.shaded.com.google.thirdparty"
+  relocate "com.sangupta", "org.apache.kudu.shaded.com.sangupta"
+  relocate "org.jboss.netty", "org.apache.kudu.shaded.org.jboss.netty"
+}
+
+// ------------------------------------------------------------------
+// Everything below is a "hack" to support partial shading and
+// accurate pom generation. At some point this logic should exist
+// in the shadow plugin itself.
+// ------------------------------------------------------------------
+
+// Add a configuration to support unshaded compile dependencies.
+// By default shadow assumes all dependencies are shaded.
+configurations.create("compileUnshaded")
+configurations.shadow.extendsFrom(configurations.compileUnshaded)
+configurations.compile.extendsFrom(configurations.compileUnshaded)
+
+// Remove the shaded dependencies from the generated pom.
+// This hack allows the project to support partially shaded jars,
+// where the shadow plugin by default would remove all compile and runtime dependencies.
+tasks.withType(Upload) {
+  def installer = install.repositories.mavenInstaller
+  def deployer = uploadArchives.repositories.mavenDeployer
+
+  // Handle install and deploy in the same way.
+  [installer, deployer]*.pom*.whenConfigured { pom ->
+    def filter = shadowJar.getDependencyFilter()
+    def configs = shadowJar.getConfigurations()
+
+    def shadowDependencies = configs.collectMany {
+      // Find all dependencies included in the shadow configuration.
+      it.resolvedConfiguration.firstLevelModuleDependencies.findAll {
+        filter.isIncluded(it)
+      }
+    }
+
+    // Remove the shadow dependencies from the pom.
+    shadowDependencies.each { shaded ->
+      pom.dependencies.removeAll { dep ->
+        dep.groupId == shaded.getModuleGroup() &&
+        dep.artifactId == shaded.getModuleName() &&
+        dep.version == shaded.getModuleVersion()
+      }
+    }
+
+    // Add the explicitly unshaded dependencies from the pom.
+    def unshadedDependencies = project.configurations.compileUnshaded.resolvedConfiguration.firstLevelModuleDependencies
+    unshadedDependencies.each { unshaded ->
+      // to avoid class loading problems
+      def dependency = pom.model.class.classLoader.loadClass("org.apache.maven.model.Dependency").newInstance()
+      dependency.setGroupId(unshaded.getModuleGroup())
+      dependency.setArtifactId(unshaded.getModuleName())
+      dependency.setVersion(unshaded.getModuleVersion())
+      dependency.setScope("compile")
+      pom.dependencies.add(dependency)
+    }
+
+    // Re-sort the generated maven dependencies to make pom comparisons easier.
+    pom.dependencies = pom.dependencies.sort { dep ->
+      "$dep.scope:$dep.optional:$dep.groupId:$dep.artifactId"
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradle/tests.gradle
----------------------------------------------------------------------
diff --git a/java/gradle/tests.gradle b/java/gradle/tests.gradle
new file mode 100644
index 0000000..01e3c0d
--- /dev/null
+++ b/java/gradle/tests.gradle
@@ -0,0 +1,80 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file contains common tasks and configuration for unit and integration tests.
+
+// Support parallel unit test execution.
+test {
+  maxParallelForks = propertyWithDefault("maxParallelForks", "1").toInteger()
+}
+
+// Log all test events.
+tasks.withType(Test) {
+  testLogging {
+    events "passed", "skipped", "failed"
+    exceptionFormat = "full"
+  }
+}
+
+// Adds pattern based integration test support.
+// All test files matching the pattern "**/*IT*.java" will be run after the the other tests.
+sourceSets {
+  test {
+    java {
+      exclude "**/*IT*.java"
+    }
+  }
+  integrationTest {
+    java {
+      srcDirs = ["src/test/java"]
+      include "**/*IT*.java"
+    }
+    compileClasspath += main.output + test.output
+    runtimeClasspath += main.output + test.output
+  }
+}
+plugins.withType(ScalaPlugin) {
+  sourceSets {
+    test {
+      scala {
+        exclude "**/*IT*.scala"
+      }
+    }
+    integrationTest {
+      scala {
+        srcDirs = ["src/test/scala"]
+        include "**/*IT*.scala"
+      }
+      compileClasspath += main.output + test.output
+      runtimeClasspath += main.output + test.output
+    }
+  }
+}
+
+configurations {
+  integrationTestCompile.extendsFrom testCompile
+  integrationTestRuntime.extendsFrom testRuntime
+}
+
+task integrationTest(type: Test, group: "Verification") {
+  description = "Runs the integration tests."
+  testClassesDirs = sourceSets.integrationTest.output.classesDirs
+  classpath = sourceSets.integrationTest.runtimeClasspath
+  maxParallelForks = 1
+  mustRunAfter test
+}
+check.dependsOn(integrationTest)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradle/wrapper.gradle
----------------------------------------------------------------------
diff --git a/java/gradle/wrapper.gradle b/java/gradle/wrapper.gradle
new file mode 100644
index 0000000..4efab86
--- /dev/null
+++ b/java/gradle/wrapper.gradle
@@ -0,0 +1,64 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file contains tasks for the gradle wrapper generation.
+
+// Ensure the wrapper script is generated based on the version defined in the project
+// and not the version installed on the machine running the task.
+// Read more about the wrapper here: https://docs.gradle.org/current/userguide/gradle_wrapper.html
+task wrapper(type: Wrapper) {
+  gradleVersion = versions.gradle
+}
+
+// Custom task to inject support for downloading the gradle wrapper jar if it doesn't exist.
+// This allows us to avoid checking in the jar to our repository.
+task bootstrapWrapper() {
+  // In the doLast block so this runs when the task is called and not during project configuration.
+  doLast {
+    def wrapperJarPath = "\$APP_HOME/gradle/wrapper/gradle-wrapper.jar"
+    // Leverages the wrapper jar checked into the gradle project on github because the jar isn't available elsewhere.
+    def wrapperJarUrl = "https://raw.githubusercontent.com/gradle/gradle/v${versions.gradle}/gradle/wrapper/gradle-wrapper.jar"
+
+
+    def boostrapString = """
+      if [[ ! -e $wrapperJarPath ]]; then
+         curl -o $wrapperJarPath $wrapperJarUrl
+      fi
+      """.stripIndent()
+
+    def wrapperScript = file("$rootDir/gradlew")
+    def wrapperLines = wrapperScript.readLines()
+    wrapperScript.withPrintWriter { out ->
+      def bootstrapWritten = false
+      wrapperLines.each { line ->
+        // Print the wrapper bootstrap before the first usage of the wrapper jar.
+        if (!bootstrapWritten && line.contains("gradle-wrapper.jar")) {
+          out.println(boostrapString)
+          bootstrapWritten = true
+        }
+        out.println(line)
+      }
+    }
+  }
+}
+wrapper.finalizedBy bootstrapWrapper
+
+// Remove the generated batch file since we don't test building in the Windows environment.
+task removeWindowScript(type: Delete) {
+  delete "$rootDir/gradlew.bat"
+}
+wrapper.finalizedBy removeWindowScript
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradle/wrapper/gradle-wrapper.properties
----------------------------------------------------------------------
diff --git a/java/gradle/wrapper/gradle-wrapper.properties b/java/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..fc47300
--- /dev/null
+++ b/java/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Aug 02 23:12:26 CDT 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-all.zip

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/gradlew
----------------------------------------------------------------------
diff --git a/java/gradlew b/java/gradlew
new file mode 100755
index 0000000..93678fd
--- /dev/null
+++ b/java/gradlew
@@ -0,0 +1,177 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+
+if [[ ! -e $APP_HOME/gradle/wrapper/gradle-wrapper.jar ]]; then
+   curl -o $APP_HOME/gradle/wrapper/gradle-wrapper.jar https://raw.githubusercontent.com/gradle/gradle/v4.0.2/gradle/wrapper/gradle-wrapper.jar
+fi
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-client-tools/build.gradle
----------------------------------------------------------------------
diff --git a/java/kudu-client-tools/build.gradle b/java/kudu-client-tools/build.gradle
new file mode 100644
index 0000000..d8627c3
--- /dev/null
+++ b/java/kudu-client-tools/build.gradle
@@ -0,0 +1,32 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+apply from: "$rootDir/gradle/shadow.gradle"
+
+dependencies {
+  compile project(":kudu-mapreduce")
+  compile libs.yetusAnnotations
+  compile libs.slf4jApi
+
+  provided libs.hadoopClient
+
+  testCompile project(path: ":kudu-client", configuration: "shadowTest")
+  testCompile project(path: ":kudu-mapreduce", configuration: "test")
+  testCompile libs.junit
+  testCompile libs.log4j
+  testCompile libs.slf4jLog4j12
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-client/build.gradle
----------------------------------------------------------------------
diff --git a/java/kudu-client/build.gradle b/java/kudu-client/build.gradle
new file mode 100644
index 0000000..b266073
--- /dev/null
+++ b/java/kudu-client/build.gradle
@@ -0,0 +1,70 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
+
+apply from: "$rootDir/gradle/protobuf.gradle"
+apply from: "$rootDir/gradle/shadow.gradle"
+
+dependencies {
+  // Not shaded in the client JAR because it's part of the public API.
+  compileUnshaded(libs.async) {
+    // async uses versions ranges for slf4j making builds non-deterministic.
+    // Remove this once the following is merged: https://github.com/OpenTSDB/async/pull/8
+    exclude group: "org.slf4j", module: "slf4j-api"
+  }
+  compileUnshaded libs.slf4jApi
+  compileUnshaded libs.yetusAnnotations
+
+  compile libs.guava
+  compile libs.murmur
+  compile libs.netty
+  compile libs.protobufJava
+
+  optional libs.jsr305
+
+  testCompile libs.commonsIo
+  testCompile libs.hamcrestCore
+  testCompile libs.junit
+  testCompile libs.log4j
+  testCompile libs.mockitoCore
+  testCompile libs.slf4jLog4j12
+}
+
+// Add protobuf files to the proto source set.
+sourceSets {
+  main {
+    proto {
+      srcDir "${project.rootDir}/../src"
+      // Excluded any test proto files
+      exclude "**/*test*.proto"
+    }
+  }
+}
+
+// Configure a shaded test jar for use in the other modules.
+// We only do this for kudu-client because it has common test utilities.
+task shadowTestJar(type: ShadowJar) {
+  classifier = "tests-shaded"
+  from sourceSets.test.output
+  configurations = [project.configurations.testRuntime]
+}
+// Create a configuration so that the shaded test jar can be referenced in other modules.
+configurations.create("shadowTest")
+artifacts {
+  shadowTest shadowTestJar
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-flume-sink/build.gradle
----------------------------------------------------------------------
diff --git a/java/kudu-flume-sink/build.gradle b/java/kudu-flume-sink/build.gradle
new file mode 100644
index 0000000..79df593
--- /dev/null
+++ b/java/kudu-flume-sink/build.gradle
@@ -0,0 +1,33 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Add the Avro plugin to support code generation from schema files.
+apply plugin: "com.commercehub.gradle.plugin.avro"
+
+dependencies {
+  compile project(path: ":kudu-client", configuration: "shadow")
+  compile libs.slf4jApi
+  compile libs.yetusAnnotations
+
+  provided libs.avro
+  provided libs.flumeConfiguration
+  provided libs.flumeCore
+  provided libs.hadoopClient
+
+  testCompile project(path: ":kudu-client", configuration: "shadowTest")
+  testCompile libs.junit
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/AvroKuduOperationsProducer.java
----------------------------------------------------------------------
diff --git a/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/AvroKuduOperationsProducer.java b/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/AvroKuduOperationsProducer.java
index ac87e29..b6241bb 100644
--- a/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/AvroKuduOperationsProducer.java
+++ b/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/AvroKuduOperationsProducer.java
@@ -59,7 +59,7 @@ import org.apache.kudu.client.PartialRow;
  * body as an Avro record and mapping its fields to columns in a Kudu table.
  *
  * <p><strong>Avro Kudu Operations Producer configuration parameters</strong>
- * <table cellpadding=3 cellspacing=0 border=1>
+ * <table cellpadding=3 cellspacing=0 border=1 summary="Avro Kudu Operations Producer configuration parameters">
  * <tr><th>Property Name</th>
  *   <th>Default</th>
  *   <th>Required?</th>

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/KuduSink.java
----------------------------------------------------------------------
diff --git a/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/KuduSink.java b/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/KuduSink.java
index 17706f7..42f0542 100644
--- a/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/KuduSink.java
+++ b/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/KuduSink.java
@@ -57,7 +57,7 @@ import org.apache.kudu.client.SessionConfiguration;
  *
  * <p><strong>Flume Kudu Sink configuration parameters</strong>
  *
- * <table cellpadding=3 cellspacing=0 border=1>
+ * <table cellpadding=3 cellspacing=0 border=1 summary="Flume Kudu Sink configuration parameters">
  * <tr><th>Property Name</th><th>Default</th><th>Required?</th><th>Description</th></tr>
  * <tr><td>channel</td><td></td><td>Yes</td><td>The name of the Flume channel to read.</td></tr>
  * <tr><td>type</td><td></td><td>Yes</td>

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/SimpleKeyedKuduOperationsProducer.java
----------------------------------------------------------------------
diff --git a/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/SimpleKeyedKuduOperationsProducer.java b/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/SimpleKeyedKuduOperationsProducer.java
index c216f99..b421771 100644
--- a/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/SimpleKeyedKuduOperationsProducer.java
+++ b/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/SimpleKeyedKuduOperationsProducer.java
@@ -43,7 +43,7 @@ import org.apache.kudu.client.Upsert;
  *
  * <p><strong>Simple Keyed Kudu Operations Producer configuration parameters</strong>
  *
- * <table cellpadding=3 cellspacing=0 border=1>
+ * <table cellpadding=3 cellspacing=0 border=1 summary="Simple Keyed Kudu Operations Producer configuration parameters">
  * <tr>
  *   <th>Property Name</th>
  *   <th>Default</th>

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/SimpleKuduOperationsProducer.java
----------------------------------------------------------------------
diff --git a/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/SimpleKuduOperationsProducer.java b/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/SimpleKuduOperationsProducer.java
index 4fa8bd8..acd74e1 100644
--- a/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/SimpleKuduOperationsProducer.java
+++ b/java/kudu-flume-sink/src/main/java/org/apache/kudu/flume/sink/SimpleKuduOperationsProducer.java
@@ -39,7 +39,7 @@ import org.apache.kudu.client.PartialRow;
  *
  * <p><strong>Simple Kudu Event Producer configuration parameters</strong>
  *
- * <table cellpadding=3 cellspacing=0 border=1>
+ * <table cellpadding=3 cellspacing=0 border=1 summary="Simple Kudu Event Producer configuration parameters">
  * <tr>
  *   <th>Property Name</th>
  *   <th>Default</th>

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-jepsen/build.gradle
----------------------------------------------------------------------
diff --git a/java/kudu-jepsen/build.gradle b/java/kudu-jepsen/build.gradle
new file mode 100644
index 0000000..fe55c21
--- /dev/null
+++ b/java/kudu-jepsen/build.gradle
@@ -0,0 +1,66 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+apply plugin: "nebula.clojure"
+
+// Skip kudu-jepsen module tasks unless "Jepsen" property is passed.
+if (!project.hasProperty("jepsen")) {
+  gradle.taskGraph.whenReady {
+    gradle.taskGraph.allTasks.each {
+      it.onlyIf { it.project != project }
+    }
+  }
+}
+
+repositories {
+  maven { url "http://clojars.org/repo/" }
+}
+
+dependencies {
+  compile project(path: ":kudu-client", configuration: "shadow")
+  compile project(path: ":kudu-client", configuration: "shadowTest")
+  compile libs.clojure
+  compile libs.clojureToolsCli
+  compile libs.jepsen
+  compile libs.yetusAnnotations
+}
+
+compileClojure {
+  aotCompile = true
+}
+// Jepsen tests require specific infrastructure and do not run as part of the regular tests.
+clojureTest.enabled = false
+
+// Run the Jepsen tests.
+task runJepsen(type: JavaExec) {
+  def masterNodes = propertyWithDefault("masterNodes", "m0")
+  def tserverNodes = propertyWithDefault("tserverNodes", "t0,t1,t2,t3,t4")
+  def sshKeyPath = propertyWithDefault("sshKeyPath", "")
+  def iterNum = propertyWithDefault("iterNum", "1")
+  classpath sourceSets.main.clojure.srcDirs,
+      sourceSets.test.clojure.srcDirs,
+      sourceSets.test.runtimeClasspath,
+      sourceSets.main.runtimeClasspath,
+  main = "clojure.main"
+  args = [
+      "$projectDir/src/utils/kudu_test_runner.clj",
+      "--masters=${masterNodes}",
+      "--tservers=${tserverNodes}",
+      "--ssh-key-path=${sshKeyPath}",
+      "--iter-num=${iterNum}"
+  ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-mapreduce/build.gradle
----------------------------------------------------------------------
diff --git a/java/kudu-mapreduce/build.gradle b/java/kudu-mapreduce/build.gradle
new file mode 100644
index 0000000..cfeb26b
--- /dev/null
+++ b/java/kudu-mapreduce/build.gradle
@@ -0,0 +1,34 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+dependencies {
+  compile project(path: ":kudu-client", configuration: "shadow")
+  compile(libs.async) {
+    // async uses versions ranges for slf4j making builds non-deterministic.
+    // Remove this once the following is merged: https://github.com/OpenTSDB/async/pull/8
+    exclude group: "org.slf4j", module: "slf4j-api"
+  }
+  compile libs.slf4jApi
+  compile libs.yetusAnnotations
+
+  provided libs.hadoopClient
+
+  testCompile project(path: ":kudu-client", configuration: "shadowTest")
+  testCompile libs.junit
+  testCompile libs.log4j
+  testCompile libs.slf4jLog4j12
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-spark-tools/build.gradle
----------------------------------------------------------------------
diff --git a/java/kudu-spark-tools/build.gradle b/java/kudu-spark-tools/build.gradle
new file mode 100644
index 0000000..adf94b7
--- /dev/null
+++ b/java/kudu-spark-tools/build.gradle
@@ -0,0 +1,37 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+apply plugin: "scala"
+apply from: "$rootDir/gradle/shadow.gradle"
+
+dependencies {
+  compile project(path: ":kudu-client", configuration: "shadow")
+  compile project(path: ":kudu-client-tools", configuration: "shadow")
+  compile project(path: ":kudu-spark", configuration: "shadow")
+  compile libs.slf4jApi
+
+  provided libs.scalaLibrary
+  provided libs.sparkCore
+  provided libs.sparkSql
+
+  testCompile project(path: ":kudu-client", configuration: "shadowTest")
+  testCompile project(path: ":kudu-spark", configuration: "test")
+  testCompile libs.junit
+  testCompile libs.log4j
+  testCompile libs.scalatest
+  testCompile libs.slf4jLog4j12
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-spark-tools/pom.xml
----------------------------------------------------------------------
diff --git a/java/kudu-spark-tools/pom.xml b/java/kudu-spark-tools/pom.xml
index e0aa13b..c2eb57f 100644
--- a/java/kudu-spark-tools/pom.xml
+++ b/java/kudu-spark-tools/pom.xml
@@ -132,6 +132,12 @@
                 <configuration>
                     <charset>${project.build.sourceEncoding}</charset>
                     <scalaVersion>${scala-2.11.version}</scalaVersion>
+                    <args>
+                        <!-- Emit warning and location for usages of features that should be imported explicitly. -->
+                        <arg>-feature</arg>
+                        <!-- Emit various static analysis warnings. -->
+                        <arg>-Xlint</arg>
+                    </args>
                 </configuration>
                 <executions>
                     <execution>

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-spark-tools/src/test/scala/org/apache/kudu/spark/tools/ITBigLinkedListTest.scala
----------------------------------------------------------------------
diff --git a/java/kudu-spark-tools/src/test/scala/org/apache/kudu/spark/tools/ITBigLinkedListTest.scala b/java/kudu-spark-tools/src/test/scala/org/apache/kudu/spark/tools/ITBigLinkedListTest.scala
new file mode 100644
index 0000000..6b34a5a
--- /dev/null
+++ b/java/kudu-spark-tools/src/test/scala/org/apache/kudu/spark/tools/ITBigLinkedListTest.scala
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kudu.spark.tools
+
+import org.apache.kudu.client.SessionConfiguration.FlushMode
+import org.apache.kudu.mapreduce.tools.BigLinkedListCommon._
+import org.apache.kudu.spark.kudu.TestContext
+import org.junit.Assert._
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+import org.scalatest.{FunSuite, Matchers}
+
+import scala.collection.JavaConverters._
+
+@RunWith(classOf[JUnitRunner])
+class ITBigLinkedListTest extends FunSuite with TestContext with Matchers {
+
+  test("Spark ITBLL") {
+    Generator.testMain(Array("--tasks=2",
+                             "--lists=2",
+                             "--nodes=10000",
+                             "--hash-partitions=2",
+                             "--range-partitions=2",
+                             "--replicas=1",
+                            s"--master-addrs=${miniCluster.getMasterAddresses}"),
+                       sc)
+
+    // Insert bad nodes in order to test the verifier:
+    //
+    //  (0, 0) points to an undefined node (-1, -1)
+    //  (0, 1) points to (0, 0)
+    //  (0, 2) points to (0, 0)
+    //
+    // Thus, (-1, -1) is undefined, (0, 0) is overreferenced,
+    // and (0, 1) and (0, 2) are unreferenced.
+
+    val table = kuduClient.openTable(DEFAULT_TABLE_NAME)
+    val session = kuduClient.newSession()
+    session.setFlushMode(FlushMode.MANUAL_FLUSH)
+
+    for ((key1, key2, prev1, prev2) <- List((0, 0, -1, -1),
+                                            (0, 1, 0, 0),
+                                            (0, 2, 0, 0))) {
+      val insert = table.newInsert()
+      insert.getRow.addLong(COLUMN_KEY_ONE_IDX, key1)
+      insert.getRow.addLong(COLUMN_KEY_TWO_IDX, key2)
+      insert.getRow.addLong(COLUMN_PREV_ONE_IDX, prev1)
+      insert.getRow.addLong(COLUMN_PREV_TWO_IDX, prev2)
+      insert.getRow.addLong(COLUMN_ROW_ID_IDX, -1)
+      insert.getRow.addString(COLUMN_CLIENT_IDX, "bad-nodes")
+      insert.getRow.addInt(COLUMN_UPDATE_COUNT_IDX, 0)
+      session.apply(insert)
+    }
+
+    for (response <- session.flush().asScala) {
+      if (response.hasRowError) {
+        // This might indicate that the generated linked lists overlapped with
+        // the bad nodes, but the odds are low.
+        throw new AssertionError(response.getRowError.getErrorStatus.toString)
+      }
+    }
+
+    val counts = Verifier.testMain(Array(s"--master-addrs=${miniCluster.getMasterAddresses}"), sc)
+    assertEquals(2 * 2 * 10000, counts.referenced)
+    assertEquals(1, counts.extrareferences)
+    assertEquals(2, counts.unreferenced)
+    assertEquals(1, counts.undefined)
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-spark-tools/src/test/scala/org/apache/kudu/spark/tools/IntegrationTestBigLinkedListTest.scala
----------------------------------------------------------------------
diff --git a/java/kudu-spark-tools/src/test/scala/org/apache/kudu/spark/tools/IntegrationTestBigLinkedListTest.scala b/java/kudu-spark-tools/src/test/scala/org/apache/kudu/spark/tools/IntegrationTestBigLinkedListTest.scala
deleted file mode 100644
index 9d1faa5..0000000
--- a/java/kudu-spark-tools/src/test/scala/org/apache/kudu/spark/tools/IntegrationTestBigLinkedListTest.scala
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.kudu.spark.tools
-
-import org.apache.kudu.client.SessionConfiguration.FlushMode
-import org.apache.kudu.mapreduce.tools.BigLinkedListCommon._
-import org.apache.kudu.spark.kudu.TestContext
-import org.junit.Assert._
-import org.junit.runner.RunWith
-import org.scalatest.junit.JUnitRunner
-import org.scalatest.{FunSuite, Matchers}
-
-import scala.collection.JavaConverters._
-
-@RunWith(classOf[JUnitRunner])
-class IntegrationTestBigLinkedListTest extends FunSuite with TestContext with Matchers {
-
-  test("Spark ITBLL") {
-    Generator.testMain(Array("--tasks=2",
-                             "--lists=2",
-                             "--nodes=10000",
-                             "--hash-partitions=2",
-                             "--range-partitions=2",
-                             "--replicas=1",
-                            s"--master-addrs=${miniCluster.getMasterAddresses}"),
-                       sc)
-
-    // Insert bad nodes in order to test the verifier:
-    //
-    //  (0, 0) points to an undefined node (-1, -1)
-    //  (0, 1) points to (0, 0)
-    //  (0, 2) points to (0, 0)
-    //
-    // Thus, (-1, -1) is undefined, (0, 0) is overreferenced,
-    // and (0, 1) and (0, 2) are unreferenced.
-
-    val table = kuduClient.openTable(DEFAULT_TABLE_NAME)
-    val session = kuduClient.newSession()
-    session.setFlushMode(FlushMode.MANUAL_FLUSH)
-
-    for ((key1, key2, prev1, prev2) <- List((0, 0, -1, -1),
-                                            (0, 1, 0, 0),
-                                            (0, 2, 0, 0))) {
-      val insert = table.newInsert()
-      insert.getRow.addLong(COLUMN_KEY_ONE_IDX, key1)
-      insert.getRow.addLong(COLUMN_KEY_TWO_IDX, key2)
-      insert.getRow.addLong(COLUMN_PREV_ONE_IDX, prev1)
-      insert.getRow.addLong(COLUMN_PREV_TWO_IDX, prev2)
-      insert.getRow.addLong(COLUMN_ROW_ID_IDX, -1)
-      insert.getRow.addString(COLUMN_CLIENT_IDX, "bad-nodes")
-      insert.getRow.addInt(COLUMN_UPDATE_COUNT_IDX, 0)
-      session.apply(insert)
-    }
-
-    for (response <- session.flush().asScala) {
-      if (response.hasRowError) {
-        // This might indicate that the generated linked lists overlapped with
-        // the bad nodes, but the odds are low.
-        throw new AssertionError(response.getRowError.getErrorStatus.toString)
-      }
-    }
-
-    val counts = Verifier.testMain(Array(s"--master-addrs=${miniCluster.getMasterAddresses}"), sc)
-    assertEquals(2 * 2 * 10000, counts.referenced)
-    assertEquals(1, counts.extrareferences)
-    assertEquals(2, counts.unreferenced)
-    assertEquals(1, counts.undefined)
-  }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-spark/build.gradle
----------------------------------------------------------------------
diff --git a/java/kudu-spark/build.gradle b/java/kudu-spark/build.gradle
new file mode 100644
index 0000000..0ce65ac
--- /dev/null
+++ b/java/kudu-spark/build.gradle
@@ -0,0 +1,54 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+apply plugin: "scala"
+apply from: "$rootDir/gradle/shadow.gradle"
+
+dependencies {
+  compile project(path: ":kudu-client", configuration: "shadow")
+  compile libs.yetusAnnotations
+
+  provided libs.scalaLibrary
+  provided libs.sparkCore
+  provided libs.sparkSql
+
+  testCompile project(path: ":kudu-client", configuration: "shadowTest")
+  testCompile libs.junit
+  testCompile libs.scalatest
+}
+
+// Add compatibility sources based on the spark version used.
+sourceSets {
+  main {
+    scala {
+      if (versions.sparkBase == "1") {
+        srcDir "src/main/spark1"
+      } else if (versions.sparkBase == "2") {
+        srcDir "src/main/spark2"
+      } else {
+        throw new GradleException("No compatibility sources exist for spark base version ${versions.sparkBase}")
+      }
+    }
+  }
+}
+
+// Adjust the artifact name to match the maven build.
+if (versions.sparkBase == "1") {
+  archivesBaseName = "kudu-spark_${versions.scalaBase}"
+} else {
+  archivesBaseName = "kudu-spark${versions.sparkBase}_${versions.scalaBase}"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/kudu-spark/pom.xml
----------------------------------------------------------------------
diff --git a/java/kudu-spark/pom.xml b/java/kudu-spark/pom.xml
index 703ff5a..7585940 100644
--- a/java/kudu-spark/pom.xml
+++ b/java/kudu-spark/pom.xml
@@ -101,6 +101,12 @@
                 <configuration>
                     <charset>${project.build.sourceEncoding}</charset>
                     <scalaVersion>${scala.version}</scalaVersion>
+                    <args>
+                        <!-- Emit warning and location for usages of features that should be imported explicitly. -->
+                        <arg>-feature</arg>
+                        <!-- Emit various static analysis warnings. -->
+                        <arg>-Xlint</arg>
+                    </args>
                 </configuration>
                 <executions>
                     <execution>

http://git-wip-us.apache.org/repos/asf/kudu/blob/e5202d30/java/settings.gradle
----------------------------------------------------------------------
diff --git a/java/settings.gradle b/java/settings.gradle
new file mode 100644
index 0000000..430e884
--- /dev/null
+++ b/java/settings.gradle
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file contains the configuration of the project hierarchy.
+// Mainly we just define what subprojects are in the build.
+
+rootProject.name = "kudu-parent"
+include "kudu-client"
+include "kudu-client-tools"
+include "kudu-flume-sink"
+include "kudu-jepsen"
+include "kudu-mapreduce"
+include "kudu-spark"
+include "kudu-spark-tools"