You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by dw...@apache.org on 2019/12/02 14:35:48 UTC

[lucene-solr] 01/01: Initial gradle build layer.

This is an automated email from the ASF dual-hosted git repository.

dweiss pushed a commit to branch gradle-master
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git

commit d4a9842375080c1a78404d9353455543d808a60b
Author: Dawid Weiss <dw...@apache.org>
AuthorDate: Mon Dec 2 15:34:57 2019 +0100

    Initial gradle build layer.
---
 .gitattributes                                     |   2 +
 .gitignore                                         |   3 +
 .travis.yml                                        |  17 ++
 build.gradle                                       |  49 +++
 gradle.TODO                                        |  52 ++++
 gradle.properties                                  |   4 +
 gradle/ant-compat/artifact-naming.gradle           |  15 +
 gradle/ant-compat/folder-layout.gradle             |  26 ++
 gradle/ant-compat/misc.gradle                      |  42 +++
 gradle/ant-compat/post-jar.gradle                  |  34 +++
 gradle/ant-compat/resolve.gradle                   | 210 +++++++++++++
 gradle/ant-compat/test-classes-cross-deps.gradle   |  53 ++++
 gradle/buildscan.gradle                            |   5 +
 gradle/defaults-idea.gradle                        |  12 +
 gradle/defaults-java.gradle                        |  11 +
 gradle/defaults-maven.gradle                       | 121 ++++++++
 gradle/defaults.gradle                             |  18 ++
 gradle/help.gradle                                 |  30 ++
 gradle/maven-local.gradle                          |  42 +++
 gradle/testing/defaults-tests-solr.gradle          |  13 +
 gradle/testing/defaults-tests.gradle               |  51 ++++
 gradle/testing/per-project-summary.gradle          |  20 ++
 gradle/testing/randomization.gradle                |  72 +++++
 gradle/testing/slowest-tests-at-end.gradle         |  31 ++
 gradle/travis.gradle                               |  21 ++
 gradle/wrapper/gradle-wrapper.jar                  | Bin 0 -> 56177 bytes
 gradle/wrapper/gradle-wrapper.properties           |   5 +
 gradlew                                            | 172 +++++++++++
 gradlew.bat                                        |  84 ++++++
 help/ant.txt                                       |  49 +++
 help/tests.txt                                     |  83 ++++++
 help/workflow.txt                                  |  33 +++
 lucene/analysis/common/build.gradle                |   7 +
 lucene/analysis/icu/build.gradle                   |  10 +
 lucene/analysis/kuromoji/build.gradle              |   8 +
 lucene/analysis/morfologik/build.gradle            |  13 +
 lucene/analysis/nori/build.gradle                  |   8 +
 lucene/analysis/opennlp/build.gradle               |   9 +
 lucene/analysis/phonetic/build.gradle              |  11 +
 lucene/analysis/smartcn/build.gradle               |   8 +
 lucene/analysis/stempel/build.gradle               |   8 +
 lucene/backward-codecs/build.gradle                |   7 +
 lucene/benchmark/build.gradle                      |  22 ++
 lucene/build.gradle                                |   3 +
 lucene/classification/build.gradle                 |  12 +
 lucene/codecs/build.gradle                         |   6 +
 lucene/core/build.gradle                           |   7 +
 lucene/demo/build.gradle                           |  12 +
 lucene/expressions/build.gradle                    |  20 ++
 lucene/facet/build.gradle                          |  12 +
 lucene/grouping/build.gradle                       |  10 +
 lucene/highlighter/build.gradle                    |  12 +
 lucene/join/build.gradle                           |   6 +
 lucene/luke/build.gradle                           |  16 +
 lucene/memory/build.gradle                         |   9 +
 lucene/misc/build.gradle                           |   6 +
 lucene/monitor/build.gradle                        |  11 +
 lucene/queries/build.gradle                        |   8 +
 lucene/queryparser/build.gradle                    |   9 +
 lucene/replicator/build.gradle                     |  19 ++
 lucene/sandbox/build.gradle                        |   6 +
 lucene/spatial-extras/build.gradle                 |  14 +
 lucene/spatial/build.gradle                        |   6 +
 lucene/spatial3d/build.gradle                      |   6 +
 lucene/suggest/build.gradle                        |   8 +
 lucene/test-framework/build.gradle                 |  11 +
 settings.gradle                                    |  55 ++++
 solr/build.gradle                                  |   3 +
 solr/contrib/analysis-extras/build.gradle          |  16 +
 solr/contrib/analytics/build.gradle                |   6 +
 solr/contrib/clustering/build.gradle               |  12 +
 solr/contrib/dataimporthandler-extras/build.gradle |  14 +
 solr/contrib/dataimporthandler/build.gradle        |  15 +
 solr/contrib/extraction/build.gradle               |  55 ++++
 .../contrib/jaegertracer-configurator/build.gradle |  12 +
 solr/contrib/langid/build.gradle                   |  15 +
 solr/contrib/ltr/build.gradle                      |  14 +
 solr/contrib/prometheus-exporter/build.gradle      |  28 ++
 solr/contrib/velocity/build.gradle                 |  14 +
 solr/core/build.gradle                             | 129 ++++++++
 solr/example/build.gradle                          |  46 +++
 solr/packaging/build.gradle                        |  95 ++++++
 solr/server/build.gradle                           | 107 +++++++
 solr/solr-ref-guide/build.gradle                   | 328 +++++++++++++++++++++
 solr/solrj/build.gradle                            |  55 ++++
 solr/test-framework/build.gradle                   |  15 +
 solr/webapp/build.gradle                           |  37 +++
 versions.lock                                      | 230 +++++++++++++++
 versions.props                                     | 111 +++++++
 89 files changed, 3132 insertions(+)

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..e37d866
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Ignore all differences in line endings for the lock file.
+versions.lock text eol=lf
diff --git a/.gitignore b/.gitignore
index d0f0ade..023c827 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,6 @@ __pycache__
 /dev-tools/scripts/scripts.iml
 .DS_Store
 
+build/
+.gradle/
+.idea/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..6cab06f
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,17 @@
+language: java
+
+jdk:
+  - openjdk11
+
+before_cache:
+  - rm -f  $HOME/.gradle/caches/modules-2/modules-2.lock
+  - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
+
+cache:
+  directories:
+    - $HOME/.gradle/caches/
+    - $HOME/.gradle/wrapper/
+
+script:
+  - ./gradlew assemble --scan --stacktrace -Ptravis
+
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..04c83cf
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,49 @@
+plugins {
+  id "base"
+  id "com.palantir.consistent-versions" version "1.12.4"
+  id "com.gradle.build-scan" version "3.0"
+}
+
+// Project version and main properties. Applies to all projects.
+allprojects {
+  version = "9.0.0-SNAPSHOT"
+}
+
+// Include smaller chunks configuring dedicated build areas.
+// Some of these intersect or add additional functionality.
+// The order of inclusion of these files shouldn't matter (but may
+// if the build file is incorrectly written and evaluates something
+// eagerly).
+
+// CI systems.
+apply from: file('gradle/buildscan.gradle')
+apply from: file('gradle/travis.gradle')
+
+// Set up defaults and configure aspects for certain modules or functionality
+// (java, tests)
+apply from: file('gradle/defaults.gradle')
+apply from: file('gradle/defaults-java.gradle')
+apply from: file('gradle/testing/defaults-tests.gradle')
+apply from: file('gradle/testing/defaults-tests-solr.gradle')
+apply from: file('gradle/testing/randomization.gradle')
+apply from: file('gradle/defaults-maven.gradle')
+
+// IDE settings and specials.
+apply from: file('gradle/defaults-idea.gradle')
+
+// Additional development aids.
+apply from: file('gradle/maven-local.gradle')
+apply from: file('gradle/testing/per-project-summary.gradle')
+apply from: file('gradle/testing/slowest-tests-at-end.gradle')
+apply from: file('gradle/help.gradle')
+
+// Ant-compatibility layer. ALL OF THESE SHOULD BE GONE at some point. They are
+// here so that we can coexist with current ant build but they are indicative
+// of potential problems with the build conventions, dependencies, etc.
+apply from: file('gradle/ant-compat/folder-layout.gradle')
+apply from: file('gradle/ant-compat/misc.gradle')
+apply from: file('gradle/ant-compat/resolve.gradle')
+apply from: file('gradle/ant-compat/post-jar.gradle')
+apply from: file('gradle/ant-compat/test-classes-cross-deps.gradle')
+apply from: file('gradle/ant-compat/artifact-naming.gradle')
+
diff --git a/gradle.TODO b/gradle.TODO
new file mode 100644
index 0000000..4dffe42
--- /dev/null
+++ b/gradle.TODO
@@ -0,0 +1,52 @@
+
+The gradle build is currently missing or could use some love in the following areas:
+
+- Apply forbiddenAPIs
+
+- configure security policy/ sandboxing for tests (!).
+
+- add test 'beasting' (rerunning the same suite multiple times). I'm afraid it'll be difficult
+  to run it sensibly because gradle doesn't offer cwd separation for the forked test runners (?)
+
+- jar checksums, jar checksum computation and validation.
+  this should be done without intermediate folders (directly
+  on dependency sets).
+
+- add a :helpDeps explanation to how the dependency system works (palantir plugin, lockfile) and
+  how to retrieve structured information about current dependencies of a given module 
+  (in a tree-like output).
+
+- identify and list precommit tasks so that they can be ported one by one.
+
+- identify and port any other "check" utilities that may be called from ant.
+
+- identify and port various "regenerate" tasks from ant builds (javacc, precompiled automata, etc.)
+
+- add rendering of javadocs (gradlew javadoc) and attaching them to maven publications.
+
+- fill in POM details in gradle/defaults-maven.gradle so that they reflect the previous content better
+  (dependencies aside).
+
+- Add any IDE integration layers that should be added (I use IntelliJ and it imports the project
+  out of the box, without the need for any special tuning).
+
+- Clean up dependencies, especially for Solr: any { transitive = false } should just explicitly
+  exclude whatever they don't need (and their dependencies currently declared explicitly
+  should be folded). Figure out which scope to import a dependency to.
+
+- add Solr packaging for docs/* (see TODO in packaging/build.gradle; currently XSLT...)
+
+- I didn't bother adding Solr dist/test-framework to packaging (who'd use it from a binary
+  distribution?)
+
+Intentional differences:
+
+- the back-compatibility target 'resolve' is added to gradle but it's really for informational purposes
+  and debugging. Packaging should be done from subcomponent configurations and dependencies,
+  not from source folders... "gradlew -p packaging assemble" puts together the entire Solr distribution
+  under packaging/build where it doesn't interfere with sources.
+
+  'resolve' for Lucene  also does *not* copy test dependencies under lib/ (like ant version does).
+
+- transitive export of certain core libraries from solr-core/ solrj (guava, etc.).
+
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..928cfe4
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,4 @@
+systemProp.file.encoding=UTF-8
+
+org.gradle.parallel=true
+org.gradle.priority=low
diff --git a/gradle/ant-compat/artifact-naming.gradle b/gradle/ant-compat/artifact-naming.gradle
new file mode 100644
index 0000000..9fe4f39
--- /dev/null
+++ b/gradle/ant-compat/artifact-naming.gradle
@@ -0,0 +1,15 @@
+// Stick to previous artifact names (not identical to path/ folders).
+configure(subprojects.findAll { it.path.contains(':solr:contrib:') }) {
+  project.archivesBaseName = project.archivesBaseName.replace("-contrib-", "-")
+}
+
+// This project has a different artifact name (solr-contrib-cell). Don't know why.
+configure(project(":solr:contrib:extraction")) {
+  archivesBaseName = "solr-cell"
+}
+
+configure(subprojects.findAll { it.path.contains(':lucene:analysis:') }) {
+  project.archivesBaseName = project.archivesBaseName.replace("-analysis-", "-analyzers-")
+}
+
+
diff --git a/gradle/ant-compat/folder-layout.gradle b/gradle/ant-compat/folder-layout.gradle
new file mode 100644
index 0000000..d6410a9
--- /dev/null
+++ b/gradle/ant-compat/folder-layout.gradle
@@ -0,0 +1,26 @@
+// Adapt to custom folder convention.
+allprojects {
+  plugins.withType(JavaPlugin) {
+    sourceSets {
+      main.java.srcDirs = ['src/java']
+      main.resources.srcDirs = ['src/resources']
+      test.java.srcDirs = ['src/test']
+      test.resources.srcDirs = ['src/test-files']
+    }
+
+    task copyTestResources(type: Copy) {
+      from('src/test') {
+        exclude '**/*.java'
+      }
+      into sourceSets.test.java.outputDir
+    }
+    processTestResources.dependsOn copyTestResources
+  }
+}
+
+// Adapt to custom 'web' folder location.
+configure(project(":solr:webapp")) {
+  plugins.withType(WarPlugin) {
+    webAppDirName = "web"
+  }
+}
diff --git a/gradle/ant-compat/misc.gradle b/gradle/ant-compat/misc.gradle
new file mode 100644
index 0000000..7ffadb2
--- /dev/null
+++ b/gradle/ant-compat/misc.gradle
@@ -0,0 +1,42 @@
+
+// Exclude inner classes from testing.
+allprojects {
+  tasks.withType(Test) { task ->
+    exclude '**/*$*'
+  }
+}
+
+// Exclude test classes that are not actually stand-alone tests (they're executed from other stuff).
+configure(project(":lucene:replicator")) {
+  plugins.withType(JavaPlugin) {
+    test {
+      exclude "**/SimpleServer*"
+    }
+  }
+}
+
+
+// Resources from top-level project folder are looked up via getClass(). Strange.
+configure(project(":lucene:benchmark")) {
+  plugins.withType(JavaPlugin) {
+    task syncConf(type: Sync) {
+      from('conf')
+      into file("${sourceSets.test.java.outputDir}/conf")
+    }
+    processTestResources.dependsOn syncConf
+  }
+}
+
+// lucene:replicator has httpclient dependency with transitive commons-logging:1.2 but currently
+// requires 1.1.2. This commons-logging should be removed entirely and replaced with slf4j-to-*
+// redirector.
+configure(project(":lucene:replicator")) {
+  plugins.withType(JavaPlugin) {
+    configurations.all {
+      resolutionStrategy {
+        force 'commons-logging:commons-logging:1.1.3'
+      }
+    }
+  }
+}
+
diff --git a/gradle/ant-compat/post-jar.gradle b/gradle/ant-compat/post-jar.gradle
new file mode 100644
index 0000000..991a979
--- /dev/null
+++ b/gradle/ant-compat/post-jar.gradle
@@ -0,0 +1,34 @@
+// This adds a configuration and artifact to solr-core which exports "post.jar" tool.
+// this should be a separate project instead (it is self-contained and classes are reused
+// in many places).
+
+configure(project(":solr:core")) {
+  plugins.withType(JavaPlugin) {
+    configurations {
+      postJar
+    }
+
+    task assemblePostJar(type: Jar) {
+      dependsOn classes
+
+      archiveFileName = "post.jar"
+      destinationDirectory = file("${buildDir}/postJar")
+
+      from(sourceSets.main.output, {
+        include "org/apache/solr/util/CLIO.class"
+        include "org/apache/solr/util/SimplePostTool*.class"
+        include "org/apache/solr/util/RTimer*.class"
+      })
+
+      manifest {
+        attributes("Main-Class": "org.apache.solr.util.SimplePostTool")
+      }
+    }
+
+    artifacts {
+      postJar assemblePostJar
+    }
+
+    assemble.dependsOn assemblePostJar
+  }
+}
\ No newline at end of file
diff --git a/gradle/ant-compat/resolve.gradle b/gradle/ant-compat/resolve.gradle
new file mode 100644
index 0000000..051bdff
--- /dev/null
+++ b/gradle/ant-compat/resolve.gradle
@@ -0,0 +1,210 @@
+
+// For Lucene, a 'resolve' task that copies any (non-project) dependencies
+// under lib/ folder.
+configure(allprojects.findAll {project -> project.path.startsWith(":lucene") }) {
+  plugins.withType(JavaPlugin) {
+    configurations {
+      runtimeLibs {
+        extendsFrom runtimeElements
+        extendsFrom testRuntimeClasspath
+      }
+    }
+
+    task resolve(type: Sync) {
+      from({
+        return configurations.runtimeLibs.copyRecursive { dep ->
+          !(dep instanceof org.gradle.api.artifacts.ProjectDependency)
+        }
+      })
+
+      into 'lib'
+    }
+  }
+}
+
+// For Solr, a 'resolve' task is much more complex. There are three folders:
+// lib/
+// test-lib/
+// lucene-libs/
+//
+// There doesn't seem to be one ideal set of rules on how these should be created, but
+// I tried to imitate the current (master) logic present in ivy and ant files in this way:
+//
+// The "solr platform" set of dependencies is a union of all deps for (core, solrj, server).
+//
+// Then:
+// lib - these are module's "own" dependencies, excluding Lucene's that are not present in the
+//       solr platform.
+// lucene-libs - these are lucene modules declared as module's dependencies and not
+//       present in solr platform.
+// test-lib/ - libs not present in solr platform and not included in solr:test-framework.
+//
+// None of these are really needed with gradle... they should be collected just in the distribution
+// package, not at each project's level.
+//
+// Unfortunately this "resolution" process is also related to how the final Solr packaging is assembled.
+// I don't know how to untie these two cleanly.
+//
+
+configure(allprojects.findAll {project -> project.path.startsWith(":solr:contrib") }) {
+  plugins.withType(JavaPlugin) {
+    ext {
+      packagingDir = file("${buildDir}/packaging")
+      deps = file("${packagingDir}/${project.name}")
+    }
+
+    configurations {
+      solrPlatformLibs
+      solrTestPlatformLibs
+      runtimeLibs {
+        extendsFrom runtimeElements
+      }
+      packaging
+    }
+
+    dependencies {
+      solrPlatformLibs project(":solr:core")
+      solrPlatformLibs project(":solr:solrj")
+      solrPlatformLibs project(":solr:server")
+
+      solrTestPlatformLibs project(":solr:test-framework")
+    }
+
+    // An aggregate that configures lib, lucene-libs and test-lib in a temporary location.
+    task assemblePackaging(type: Sync) {
+      from "README.txt"
+
+      from ({
+        def externalLibs = configurations.runtimeLibs.copyRecursive { dep ->
+          if (dep instanceof org.gradle.api.artifacts.ProjectDependency) {
+            return !dep.dependencyProject.path.startsWith(":solr")
+          } else {
+            return true
+          }
+        }
+        return externalLibs - configurations.solrPlatformLibs
+      }, {
+        exclude "lucene-*"
+        into "lib"
+      })
+
+      from ({
+        def projectLibs = configurations.runtimeLibs.copyRecursive { dep ->
+          (dep instanceof org.gradle.api.artifacts.ProjectDependency)
+        }
+        return projectLibs - configurations.solrPlatformLibs
+      }, {
+        include "lucene-*"
+        into "lucene-libs"
+      })
+
+      into deps
+    }
+
+    task syncLib(type: Sync) {
+      dependsOn assemblePackaging
+
+      from(file("${deps}/lib"), {
+        include "**"
+      })
+      into file("${projectDir}/lib")
+    }
+
+    task syncTestLib(type: Sync) {
+      // From test runtime classpath exclude:
+      // 1) project dependencies (and their dependencies)
+      // 2) runtime dependencies
+      // What remains is this module's "own" test dependency.
+      from({
+        def testRuntimeLibs = configurations.testRuntimeClasspath.copyRecursive { dep ->
+          !(dep instanceof org.gradle.api.artifacts.ProjectDependency)
+        }
+
+        return testRuntimeLibs - configurations.runtimeLibs - configurations.solrTestPlatformLibs
+      })
+
+      into file("${projectDir}/test-lib")
+    }
+
+    task resolve() {
+      dependsOn syncLib, syncTestLib
+    }
+
+    // Contrib packaging currently depends on internal resolve.
+    artifacts {
+      packaging packagingDir, {
+        builtBy assemblePackaging
+      }
+    }
+  }
+}
+
+configure(project(":solr:example")) {
+  evaluationDependsOn(":solr:example") // explicitly wait for other configs to be applied
+
+  task resolve(type: Copy) {
+    from(configurations.postJar, {
+      into "exampledocs/"
+    })
+
+    from(configurations.dih, {
+      into "example-DIH/solr/db/lib"
+    })
+
+    into projectDir
+  }
+}
+
+configure(project(":solr:server")) {
+  evaluationDependsOn(":solr:server")
+
+  task resolve(type: Copy) {
+    dependsOn assemblePackaging
+
+    from({ packagingDir }, {
+      include "**/*.jar"
+      include "solr-webapp/webapp/**"
+      includeEmptyDirs false
+    })
+
+    into projectDir
+  }
+}
+
+configure(project(":solr:core")) {
+  evaluationDependsOn(":solr:core")
+
+  configurations {
+    runtimeLibs {
+      extendsFrom runtimeElements
+    }
+  }
+
+  task resolve(type: Sync) {
+    from({
+      def ownDeps = configurations.runtimeLibs.copyRecursive { dep ->
+        if (dep instanceof org.gradle.api.artifacts.ProjectDependency) {
+          return !dep.dependencyProject.path.startsWith(":solr")
+        } else {
+          return true
+        }
+      }
+      return ownDeps
+    }, {
+      exclude "lucene-*"
+    })
+
+    into "lib"
+  }
+}
+
+configure(project(":solr:solrj")) {
+  evaluationDependsOn(":solr:solrj")
+
+  task resolve(type: Sync) {
+    from({ configurations.runtimeClasspath }, {
+    })
+
+    into "lib"
+  }
+}
\ No newline at end of file
diff --git a/gradle/ant-compat/test-classes-cross-deps.gradle b/gradle/ant-compat/test-classes-cross-deps.gradle
new file mode 100644
index 0000000..02057ac
--- /dev/null
+++ b/gradle/ant-compat/test-classes-cross-deps.gradle
@@ -0,0 +1,53 @@
+// Set up cross-project dependency on test classes. This should be resolved by pulling reused classes into
+// a separate regular module. Exporting test classes is sort of weird.
+configure([project(":lucene:spatial3d"),
+           project(":lucene:analysis:common"),
+           project(":lucene:backward-codecs"),
+           project(":lucene:queryparser"),
+           project(":solr:contrib:dataimporthandler")]) {
+  plugins.withType(JavaPlugin) {
+    configurations {
+      testClassesExported
+    }
+
+    artifacts {
+      testClassesExported sourceSets.test.java.outputDir, {
+        builtBy testClasses
+      }
+    }
+  }
+}
+
+configure(project(":lucene:spatial-extras")) {
+  plugins.withType(JavaPlugin) {
+    dependencies {
+      testImplementation project(path: ':lucene:spatial3d', configuration: 'testClassesExported')
+    }
+  }
+}
+
+configure(project(":solr:core")) {
+  plugins.withType(JavaPlugin) {
+    dependencies {
+      testImplementation project(path: ':lucene:backward-codecs', configuration: 'testClassesExported')
+      testImplementation project(path: ':lucene:queryparser', configuration: 'testClassesExported')
+    }
+  }
+}
+
+configure(project(":solr:contrib:analysis-extras")) {
+  plugins.withType(JavaPlugin) {
+    dependencies {
+      testImplementation project(path: ':lucene:analysis:common', configuration: 'testClassesExported')
+      testImplementation project(path: ':solr:contrib:dataimporthandler', configuration: 'testClassesExported')
+    }
+  }
+}
+
+configure(project(":solr:contrib:dataimporthandler-extras")) {
+  plugins.withType(JavaPlugin) {
+    dependencies {
+      testImplementation project(path: ':solr:contrib:dataimporthandler', configuration: 'testClassesExported')
+    }
+  }
+}
diff --git a/gradle/buildscan.gradle b/gradle/buildscan.gradle
new file mode 100644
index 0000000..83d0b71
--- /dev/null
+++ b/gradle/buildscan.gradle
@@ -0,0 +1,5 @@
+
+buildScan {
+    termsOfServiceUrl = "https://gradle.com/terms-of-service"
+    termsOfServiceAgree = "yes"
+}
diff --git a/gradle/defaults-idea.gradle b/gradle/defaults-idea.gradle
new file mode 100644
index 0000000..b832324
--- /dev/null
+++ b/gradle/defaults-idea.gradle
@@ -0,0 +1,12 @@
+allprojects {
+  apply plugin: 'idea'
+
+  idea {
+    module {
+      outputDir file('build/idea/classes/main')
+      testOutputDir file('build/idea/classes/test')
+      downloadSources = true
+    }
+  }
+}
+
diff --git a/gradle/defaults-java.gradle b/gradle/defaults-java.gradle
new file mode 100644
index 0000000..896b8bb
--- /dev/null
+++ b/gradle/defaults-java.gradle
@@ -0,0 +1,11 @@
+// Configure Java project defaults.
+
+allprojects {
+  plugins.withType(JavaPlugin) {
+    sourceCompatibility = "11"
+    targetCompatibility = "11"
+
+    compileJava.options.encoding = "UTF-8"
+    compileTestJava.options.encoding = "UTF-8"
+  }
+}
diff --git a/gradle/defaults-maven.gradle b/gradle/defaults-maven.gradle
new file mode 100644
index 0000000..35dcab3
--- /dev/null
+++ b/gradle/defaults-maven.gradle
@@ -0,0 +1,121 @@
+
+// Maven publications and configuration.
+//
+// the 'published' list contains an explicit list of all projects
+// which should be published to Maven repositories.
+
+configure(rootProject) {
+  ext {
+    published = [
+        ":lucene:analysis:common",
+        ":lucene:analysis:icu",
+        ":lucene:analysis:kuromoji",
+        ":lucene:analysis:morfologik",
+        ":lucene:analysis:nori",
+        ":lucene:analysis:opennlp",
+        ":lucene:analysis:phonetic",
+        ":lucene:analysis:smartcn",
+        ":lucene:analysis:stempel",
+        ":lucene:backward-codecs",
+        ":lucene:benchmark",
+        ":lucene:classification",
+        ":lucene:codecs",
+        ":lucene:core",
+        ":lucene:demo",
+        ":lucene:expressions",
+        ":lucene:facet",
+        ":lucene:grouping",
+        ":lucene:highlighter",
+        ":lucene:join",
+        ":lucene:luke",
+        ":lucene:memory",
+        ":lucene:misc",
+        ":lucene:monitor",
+        ":lucene:queries",
+        ":lucene:queryparser",
+        ":lucene:replicator",
+        ":lucene:sandbox",
+        ":lucene:spatial",
+        ":lucene:spatial-extras",
+        ":lucene:spatial3d",
+        ":lucene:suggest",
+        ":lucene:test-framework",
+
+        ":solr:core",
+        ":solr:solrj",
+        ":solr:contrib:analysis-extras",
+        ":solr:contrib:dataimporthandler",
+        ":solr:contrib:dataimporthandler-extras",
+        ":solr:contrib:analytics",
+        ":solr:contrib:clustering",
+        ":solr:contrib:extraction",
+        ":solr:contrib:langid",
+        ":solr:contrib:jaegertracer-configurator",
+        ":solr:contrib:prometheus-exporter",
+        ":solr:contrib:velocity",
+        ":solr:test-framework",
+    ]
+  }
+
+  configure(subprojects.findAll { it.path in rootProject.published }) {
+    apply plugin: 'maven-publish'
+    apply plugin: 'signing'
+
+    publishing {
+      // TODO: Add publishing repository details.
+    }
+
+    plugins.withType(JavaPlugin) {
+      task sourcesJar(type: Jar, dependsOn: classes) {
+        archiveClassifier = 'sources'
+        from sourceSets.main.allJava
+      }
+
+      task javadocJar(type: Jar, dependsOn: javadoc) {
+        archiveClassifier = 'javadoc'
+        from javadoc.destinationDir
+      }
+
+      publishing {
+        def configurePom = {
+          name = "Apache Solr/Lucene (${project.name})"
+          licenses {
+            license {
+              name = 'Apache 2'
+              url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+            }
+          }
+        }
+
+        publications {
+          // JARS and sources, no javadocs (for local inspection only).
+          jars(MavenPublication) {
+            from components.java
+            groupId = project.group
+            artifactId = project.archivesBaseName
+
+            artifact sourcesJar
+
+            pom(configurePom)
+          }
+
+          // Full set of signed artifacts.
+          signed(MavenPublication) {
+            from components.java
+            groupId = project.group
+            artifactId = project.archivesBaseName
+
+            artifact sourcesJar
+            artifact javadocJar
+
+            pom(configurePom)
+          }
+        }
+      }
+
+      signing {
+        sign publishing.publications.signed
+      }
+    }
+  }
+}
diff --git a/gradle/defaults.gradle b/gradle/defaults.gradle
new file mode 100644
index 0000000..474826e
--- /dev/null
+++ b/gradle/defaults.gradle
@@ -0,0 +1,18 @@
+allprojects {
+  apply plugin: 'base'
+
+  group "org.apache"
+
+  // Repositories to fetch dependencies from.
+  repositories {
+    mavenCentral()
+
+    maven {
+      url "https://maven.restlet.com"
+    }
+  }
+
+  // Artifacts will have names after full gradle project path
+  // so :solr:core will have solr-core.jar, etc.
+  project.archivesBaseName = project.path.replaceAll("^:", "").replace(':', '-')
+}
diff --git a/gradle/help.gradle b/gradle/help.gradle
new file mode 100644
index 0000000..01fce34
--- /dev/null
+++ b/gradle/help.gradle
@@ -0,0 +1,30 @@
+// Add "help" tasks which display plain text files under 'help' folder.
+
+configure(rootProject) {
+  def helpFiles = [
+      ["Workflow", "help/workflow.txt", "Typical workflow commands."],
+      ["Ant", "help/ant-gradle.txt", "Ant-gradle migration help."],
+      ["Tests", "help/tests.txt", "Tests, filtering, beasting, etc."],
+  ]
+
+  helpFiles.each { section, path, sectionInfo ->
+    task "help${section}" {
+      group = 'Help (developer guides and hints)'
+      description = sectionInfo
+      doFirst {
+        println "\n" + rootProject.file(path).getText("UTF-8")
+      }
+    }
+  }
+
+  help {
+    doLast {
+      println ""
+      println "This is an experimental Lucene/Solr gradle build. See some"
+      println "guidelines, ant-equivalent commands etc. under help/*; or type:"
+      helpFiles.each { entry ->
+        println "  gradlew :help${entry[0]}  # ${entry[2]}"
+      }
+    }
+  }
+}
diff --git a/gradle/maven-local.gradle b/gradle/maven-local.gradle
new file mode 100644
index 0000000..25a4746
--- /dev/null
+++ b/gradle/maven-local.gradle
@@ -0,0 +1,42 @@
+
+// This adds a root project task to install all artifacts to a build-local
+// Maven repository (so that pom files can be manually reviewed).
+
+configure(rootProject) {
+  ext {
+    mavenLocalDir = file("${buildDir}/maven-local")
+  }
+
+  task mavenLocal() {
+    group "Publishing"
+    description "Publish Maven JARs and POMs locally to " + mavenLocalDir
+
+    doLast {
+      logger.lifecycle "Local maven artifacts (poms, jars) created at: ${mavenLocalDir}"
+    }
+  }
+
+  task mavenLocalClean(type: Delete) {
+    delete mavenLocalDir
+  }
+
+  configure(subprojects.findAll { it.path in rootProject.published }) {
+    plugins.withType(PublishingPlugin) {
+      publishing {
+        repositories {
+          maven {
+            name = 'build'
+            url = mavenLocalDir
+          }
+        }
+      }
+
+      tasks.matching { it.name == "publishJarsPublicationToBuildRepository" }.all { task ->
+        // Clean prior to republishing to local build repository.
+        task.dependsOn mavenLocalClean
+        // Attach to root project's mavenLocal task.
+        mavenLocal.dependsOn task
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/gradle/testing/defaults-tests-solr.gradle b/gradle/testing/defaults-tests-solr.gradle
new file mode 100644
index 0000000..be84a59
--- /dev/null
+++ b/gradle/testing/defaults-tests-solr.gradle
@@ -0,0 +1,13 @@
+import org.apache.tools.ant.taskdefs.condition.Os
+
+// Solr-specific test configs.
+
+configure(allprojects.findAll {project -> project.path.startsWith(":solr") }) {
+  plugins.withType(JavaPlugin) {
+    test {
+      systemProperty 'tests.disableHdfs', Os.isFamily(Os.FAMILY_WINDOWS) ? 'true' : 'false'
+      systemProperty 'jetty.testMode', '1'
+      systemProperty 'jetty.insecurerandom', '1'
+    }
+  }
+}
diff --git a/gradle/testing/defaults-tests.gradle b/gradle/testing/defaults-tests.gradle
new file mode 100644
index 0000000..668cb84
--- /dev/null
+++ b/gradle/testing/defaults-tests.gradle
@@ -0,0 +1,51 @@
+import org.apache.tools.ant.taskdefs.condition.Os
+import org.gradle.api.tasks.testing.logging.*
+
+allprojects {
+  plugins.withType(JavaPlugin) {
+    project.ext {
+      testsWorkDir = file("${buildDir}/tmp/tests-cwd")
+      testsTmpDir = file("${buildDir}/tmp/tests-tmp")
+    }
+
+    test {
+      workingDir testsWorkDir
+
+      useJUnit()
+
+      // Set up default parallel execution limits.
+      maxParallelForks = (int) Math.max(1, Math.min(Runtime.runtime.availableProcessors() / 2.0, 3.0))
+
+      minHeapSize = "256m"
+      maxHeapSize = "512m"
+
+      systemProperty 'java.awt.headless', 'true'
+      systemProperty 'jdk.map.althashing.threshold', '0'
+
+      if (!Os.isFamily(Os.FAMILY_WINDOWS)) {
+        systemProperty 'java.security.egd', 'file:/dev/./urandom'
+      }
+
+      // Set up cwd and temp locations.
+      systemProperty("java.io.tmpdir", testsTmpDir)
+      doFirst {
+        testsWorkDir.mkdirs()
+        testsTmpDir.mkdirs()
+      }
+
+      // Set up logging.
+      testLogging {
+        events TestLogEvent.FAILED
+        exceptionFormat TestExceptionFormat.FULL
+        showExceptions true
+        showCauses true
+        showStackTraces true
+      }
+
+      doFirst {
+        // Print some diagnostics about locations used.
+        logger.info("Test folders for {}: cwd={}, tmp={}", project.path, testsWorkDir, testsTmpDir)
+      }
+    }
+  }
+}
diff --git a/gradle/testing/per-project-summary.gradle b/gradle/testing/per-project-summary.gradle
new file mode 100644
index 0000000..ec5281e
--- /dev/null
+++ b/gradle/testing/per-project-summary.gradle
@@ -0,0 +1,20 @@
+
+// Per-project test summary.
+
+allprojects {
+  tasks.withType(Test) { task ->
+    afterSuite { desc, result ->
+      if (!desc.parent) {
+        if (result.testCount > 0) {
+          def components = [
+              "test(s)"   : result.testCount,
+              "failure(s)": result.failedTestCount,
+              "skipped"   : result.skippedTestCount
+          ].findAll { k, v -> v > 0 }.collect { k, v -> "$v $k" }.join(", ")
+
+          logger.lifecycle("${task.path} (${result.resultType}): ${components}")
+        }
+      }
+    }
+  }
+}
diff --git a/gradle/testing/randomization.gradle b/gradle/testing/randomization.gradle
new file mode 100644
index 0000000..8024264
--- /dev/null
+++ b/gradle/testing/randomization.gradle
@@ -0,0 +1,72 @@
+
+// Configure test randomization seeds and derived test properties.
+
+allprojects {
+  ext {
+    // Support passing overrides via -P or -D.
+    propertyOrDefault = { propName, defValue ->
+      def result
+      if (project.hasProperty(propName)) {
+        result = project.getProperty(propName)
+      } else if (System.properties.containsKey(propName)) {
+        result = System.properties.get(propName)
+      } else {
+        result = defValue
+      }
+      return result
+    }
+  }
+}
+
+// Pick the "root" seed from which everything else is derived.
+configure(rootProject) {
+  ext {
+    rootSeed = propertyOrDefault('tests.seed', String.format("%08X", new Random().nextLong()))
+  }
+
+  task randomizationInfo() {
+    doFirst {
+      logger.lifecycle("Running tests with randomization seed: tests.seed=${rootSeed}")
+    }
+  }
+}
+
+// Any test task will trigger display of randomization settings.
+allprojects {
+  tasks.withType(Test) { task ->
+    task.dependsOn rootProject.randomizationInfo
+  }
+}
+
+// Append randomization properties to tests, allow overriding these properties with -Pkey=value.
+allprojects {
+  tasks.withType(Test) { task ->
+    [
+        'tests.seed': rootSeed,
+        'tests.multiplier': '1',
+        'tests.codec': 'random',
+        'tests.postingsformat': 'random',
+        'tests.docvaluesformat': 'random',
+        'tests.locale': 'random',
+        'tests.timezone': 'random',
+        'tests.directory': 'random',
+        'tests.nightly': 'false',
+        'tests.weekly': 'false',
+        'tests.monster': 'false',
+        'tests.slow': 'true',
+        'tests.verbose': 'false',
+        'tests.filterstacks': 'true',
+        'tests.asserts': 'true',
+        'tests.iters': null,
+        'tests.filter': null,
+        'tests.linedocsfile': 'europarl.lines.txt.gz',
+        'tests.cleanthreads.sysprop': 'perMethod'
+    ].each { propName, defValue ->
+      def value = propertyOrDefault(propName, defValue)
+      if (value != null) {
+        systemProperty propName, value
+      }
+    }
+  }
+}
+
diff --git a/gradle/testing/slowest-tests-at-end.gradle b/gradle/testing/slowest-tests-at-end.gradle
new file mode 100644
index 0000000..2e33917
--- /dev/null
+++ b/gradle/testing/slowest-tests-at-end.gradle
@@ -0,0 +1,31 @@
+// Add test duration summary at the end of the build.
+
+def allTests = []
+
+allprojects {
+  tasks.withType(Test) { task ->
+    afterTest { desc, result ->
+      def duration = (result.getEndTime() - result.getStartTime())
+
+      allTests << [
+        name    : "${desc.className.replaceAll('.+\\.', "")}.${desc.name} (${project.name})",
+        duration: duration
+      ]
+    }
+  }
+}
+
+gradle.buildFinished {
+  if (allTests) {
+    def slowest = allTests
+      .sort { a, b -> b.duration.compareTo(a.duration) }
+      .take(10)
+      .findAll { e -> e.duration >= 500 }
+      .collect { e -> String.format(Locale.ROOT, "%5.2fs %s", e.duration / 1000d, e.name) }
+
+    if (slowest) {
+      logger.lifecycle("The slowest tests (exceeding 500 ms) during this run:\n  " +
+        slowest.join("\n  "))
+    }
+  }
+}
diff --git a/gradle/travis.gradle b/gradle/travis.gradle
new file mode 100644
index 0000000..45d3bb2
--- /dev/null
+++ b/gradle/travis.gradle
@@ -0,0 +1,21 @@
+// Emit more periodic info for travis builds (otherwise they timeout due to
+// no input on the console).
+
+import org.gradle.api.tasks.testing.logging.*
+
+if (hasProperty("travis")) {
+  allprojects {
+    tasks.withType(Test) { task ->
+      maxParallelForks = 2
+
+      afterSuite { desc, result ->
+        if (desc.className) {
+          def tc = result.testCount
+          def tcs = (tc == 1 ? 'test' : 'tests')
+
+          logger.lifecycle("Completed: ${desc.className} (${tc} ${tcs})")
+        }
+      }
+    }
+  }
+}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..29953ea
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..51b873d
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-5.6.2-all.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/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
+
+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" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/help/ant.txt b/help/ant.txt
new file mode 100644
index 0000000..b201c7e
--- /dev/null
+++ b/help/ant.txt
@@ -0,0 +1,49 @@
+Gradle for Ant users
+====================
+
+This shows some common ant targets and their equivalent Gradle commands.
+Examples below assume cwd is at the top of the checkout (gradlew
+script available from ./). Quoted [string] gives a better or more
+conventional and commonly used task alternative.
+
+Gradle tasks apply to all modules that contain a given task name. Use
+"-p" switch with a directory or a colon-notation to specify a particular
+task or module. For example these two are equivalent:
+
+gradlew -p lucene/core check
+gradlew :lucene:core:check
+
+List of common dev task equivalents
+-----------------------------------
+
+ant clean           => gradlew clean
+ant jar             => gradlew jar          [better: gradlew assemble]
+
+ant compile         => gradlew classes      [better: gradlew assemble]
+                       gradlew testClasses  [better: gradlew assemble]
+
+ant validate        => gradlew check
+ant test            => gradlew test
+
+ant jar-checksums   => TODO
+ant clean-jars      => NO REPLACEMENT
+
+ant precommit       => gradlew check -x test
+
+ant get-maven-poms  => gradlew mavenLocal
+
+Solr-specific targets
+---------------------
+
+Assemble Solr distribution at solr/packaging/build/...
+
+ant create-package  => gradlew -p solr/packaging assemble
+
+"Resolve" libraries by copying them to lib/ and other source
+locations. This task should *not* be used anymore. It is added
+for backward compatibility with ant (and for debugging)
+but it mixes sources with output locations and this workflow
+should be discouraged. Instead run assemble on packaging (above).
+
+ant resolve         => gradlew -p solr resolve
+
diff --git a/help/tests.txt b/help/tests.txt
new file mode 100644
index 0000000..6f5938a
--- /dev/null
+++ b/help/tests.txt
@@ -0,0 +1,83 @@
+Testing
+=======
+
+Examples below assume cwd at the gradlew script in the top directory of
+the project's checkout.
+
+
+Generic test/ checkup commands
+------------------------------
+
+Run all unit tests:
+
+gradlew test
+
+Run all verification tasks, including tests:
+
+gradlew check
+
+Run all verification tasks, excluding tests (-x is gradle's generic task
+exclusion mechanism):
+
+gradlew check -x test
+
+Run verification for a selected module only:
+
+gradlew :lucene:core:check     # By full gradle project path
+gradlew -p lucene/core check   # By folder designation + task
+
+
+Randomization
+-------------
+
+Run tests with the given starting seed:
+
+gradlew :lucene:misc:test -Ptests.seed=DEADBEEF
+
+
+Filtering
+---------
+
+Run tests of lucene-core module:
+
+gradlew -p lucene/core test
+
+Run a single test case (from a single module). Uses gradle's built-in filtering
+(https://docs.gradle.org/current/userguide/java_testing.html#test_filtering):
+
+gradlew -p lucene/core test --tests TestDemo
+
+Run all tests in a package:
+
+gradlew -p lucene/core test --tests "org.apache.lucene.document.*"
+
+Run all test classes/ methods that match this pattern:
+
+gradlew -p lucene/core test --tests "*testFeatureMissing*"
+
+
+Test groups
+-----------
+
+Tests can be filtered by an annotation they're marked with.
+Some test group annotations include: @AwaitsFix, @Nightly, @Slow
+
+This uses filtering infrastructure on the *runner* (randomizedtesting), 
+not gradle's built-in mechanisms (but it can be combined with "--tests").
+For example, run all lucene-core tests annotated as @Slow:
+
+gradlew -p lucene/core test -Ptests.filter=@Slow
+
+Test group filters can be combined into Boolean expressions:
+
+gradlew -p lucene/core test "default and not(@awaitsfix or @slow)"
+
+
+Reiteration ("beasting")
+------------------------
+
+Multiply each test case N times (this works by repeating the same test
+within the same JVM; it also works in IDEs):
+
+gradlew -p lucene/core test --tests TestDemo -Ptests.iters=5
+
diff --git a/help/workflow.txt b/help/workflow.txt
new file mode 100644
index 0000000..f929a52
--- /dev/null
+++ b/help/workflow.txt
@@ -0,0 +1,33 @@
+Typical workflow and tasks
+==========================
+
+This shows some typical workflow gradle commands.
+
+Run tests on a module:
+gradlew -p lucene/core test
+
+Run test of a single-class (run "gradlew :helpTests" for more):
+gradlew -p lucene/core test --tests "*Demo*"
+
+Run all tests and validation checks on a module:
+gradlew -p lucene/core check
+
+Run all tests and validation checks on everything:
+gradlew check
+
+Run all validation checks but skip all tests:
+gradlew check -x test
+
+Assemble a single module's JAR (here for lucene-core):
+gradlew -p lucene/core assemble
+ls lucene/core/build/libs
+
+Create all distributable packages, POMs, etc. and create a
+local maven repository for inspection:
+gradlew mavenLocal
+ls -R build/maven-local/
+
+Put together Solr distribution:
+gradlew -p solr/packaging assemble
+ls solr/packaging/build/solr-*
+
diff --git a/lucene/analysis/common/build.gradle b/lucene/analysis/common/build.gradle
new file mode 100644
index 0000000..2c68c0d
--- /dev/null
+++ b/lucene/analysis/common/build.gradle
@@ -0,0 +1,7 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  testImplementation project(':lucene:test-framework')
+}
+
diff --git a/lucene/analysis/icu/build.gradle b/lucene/analysis/icu/build.gradle
new file mode 100644
index 0000000..d114f52
--- /dev/null
+++ b/lucene/analysis/icu/build.gradle
@@ -0,0 +1,10 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  api project(':lucene:analysis:common')
+
+  api 'com.ibm.icu:icu4j'
+
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/analysis/kuromoji/build.gradle b/lucene/analysis/kuromoji/build.gradle
new file mode 100644
index 0000000..1e0a9e3
--- /dev/null
+++ b/lucene/analysis/kuromoji/build.gradle
@@ -0,0 +1,8 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  api project(':lucene:analysis:common')
+
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/analysis/morfologik/build.gradle b/lucene/analysis/morfologik/build.gradle
new file mode 100644
index 0000000..ddbef7a
--- /dev/null
+++ b/lucene/analysis/morfologik/build.gradle
@@ -0,0 +1,13 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  api project(':lucene:analysis:common')
+
+  api 'org.carrot2:morfologik-stemming'
+
+  implementation 'org.carrot2:morfologik-polish'
+  implementation 'ua.net.nlp:morfologik-ukrainian-search'
+
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/analysis/nori/build.gradle b/lucene/analysis/nori/build.gradle
new file mode 100644
index 0000000..f31a53e
--- /dev/null
+++ b/lucene/analysis/nori/build.gradle
@@ -0,0 +1,8 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  api project(':lucene:analysis:common')
+  
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/analysis/opennlp/build.gradle b/lucene/analysis/opennlp/build.gradle
new file mode 100644
index 0000000..65e45ca
--- /dev/null
+++ b/lucene/analysis/opennlp/build.gradle
@@ -0,0 +1,9 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  api project(':lucene:analysis:common')
+  api 'org.apache.opennlp:opennlp-tools'
+
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/analysis/phonetic/build.gradle b/lucene/analysis/phonetic/build.gradle
new file mode 100644
index 0000000..d0d08bd
--- /dev/null
+++ b/lucene/analysis/phonetic/build.gradle
@@ -0,0 +1,11 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  api project(':lucene:analysis:common')
+
+  implementation 'commons-codec:commons-codec'
+
+  testImplementation project(':lucene:test-framework')
+} 
+
diff --git a/lucene/analysis/smartcn/build.gradle b/lucene/analysis/smartcn/build.gradle
new file mode 100644
index 0000000..d4d34df
--- /dev/null
+++ b/lucene/analysis/smartcn/build.gradle
@@ -0,0 +1,8 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  api project(':lucene:analysis:common')
+
+  testImplementation project(':lucene:test-framework')
+} 
diff --git a/lucene/analysis/stempel/build.gradle b/lucene/analysis/stempel/build.gradle
new file mode 100644
index 0000000..1e0a9e3
--- /dev/null
+++ b/lucene/analysis/stempel/build.gradle
@@ -0,0 +1,8 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  api project(':lucene:analysis:common')
+
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/backward-codecs/build.gradle b/lucene/backward-codecs/build.gradle
new file mode 100644
index 0000000..b92dea0
--- /dev/null
+++ b/lucene/backward-codecs/build.gradle
@@ -0,0 +1,7 @@
+
+apply plugin: 'java-library'
+
+dependencies { 
+  api project(':lucene:core')
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/benchmark/build.gradle b/lucene/benchmark/build.gradle
new file mode 100644
index 0000000..32daeb7
--- /dev/null
+++ b/lucene/benchmark/build.gradle
@@ -0,0 +1,22 @@
+
+apply plugin: 'java-library'
+
+dependencies {  
+  api project(':lucene:core')
+
+  implementation project(':lucene:analysis:common')
+  implementation project(':lucene:facet')
+  implementation project(':lucene:highlighter')
+  implementation project(':lucene:queries')
+  implementation project(':lucene:spatial-extras')
+  implementation project(':lucene:queryparser')
+
+  implementation "org.apache.commons:commons-compress"
+  implementation "com.ibm.icu:icu4j"
+  implementation "org.locationtech.spatial4j:spatial4j"
+  implementation("net.sourceforge.nekohtml:nekohtml", {
+    exclude module: "xml-apis"
+  })
+
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/build.gradle b/lucene/build.gradle
new file mode 100644
index 0000000..26082a2
--- /dev/null
+++ b/lucene/build.gradle
@@ -0,0 +1,3 @@
+subprojects {
+  group "org.apache.lucene"
+}
\ No newline at end of file
diff --git a/lucene/classification/build.gradle b/lucene/classification/build.gradle
new file mode 100644
index 0000000..835cd33
--- /dev/null
+++ b/lucene/classification/build.gradle
@@ -0,0 +1,12 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+
+  implementation project(':lucene:queries')
+  implementation project(':lucene:grouping')
+
+  testImplementation project(':lucene:test-framework')
+  testImplementation project(':lucene:analysis:common')
+  testImplementation project(':lucene:codecs')
+}
diff --git a/lucene/codecs/build.gradle b/lucene/codecs/build.gradle
new file mode 100644
index 0000000..50a833a
--- /dev/null
+++ b/lucene/codecs/build.gradle
@@ -0,0 +1,6 @@
+apply plugin: 'java-library'
+
+dependencies {
+    implementation project(':lucene:core')
+    testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/core/build.gradle b/lucene/core/build.gradle
new file mode 100644
index 0000000..8be3b6e
--- /dev/null
+++ b/lucene/core/build.gradle
@@ -0,0 +1,7 @@
+
+apply plugin: 'java-library'
+
+dependencies {
+  testImplementation project(':lucene:codecs')
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/demo/build.gradle b/lucene/demo/build.gradle
new file mode 100644
index 0000000..015f4c1
--- /dev/null
+++ b/lucene/demo/build.gradle
@@ -0,0 +1,12 @@
+apply plugin: 'java-library'
+
+dependencies {
+  implementation project(':lucene:core')
+  implementation project(':lucene:facet')
+  implementation project(':lucene:queries')
+  implementation project(':lucene:analysis:common')
+  implementation project(':lucene:queryparser')
+  implementation project(':lucene:expressions')
+  
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/expressions/build.gradle b/lucene/expressions/build.gradle
new file mode 100644
index 0000000..9222bf0
--- /dev/null
+++ b/lucene/expressions/build.gradle
@@ -0,0 +1,20 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+
+  implementation project(':lucene:codecs')
+
+  implementation 'org.antlr:antlr4-runtime'
+
+  // It is awkward that we force-omit the intermediate dependency here...
+  // The dependency chain is:
+  //   asm-commons -> asm-tree -> asm
+  // Should we really go through these hoops?
+  implementation 'org.ow2.asm:asm'
+  implementation('org.ow2.asm:asm-commons', {
+    exclude group: "org.ow2.asm", module: "asm-tree"
+  })
+
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/facet/build.gradle b/lucene/facet/build.gradle
new file mode 100644
index 0000000..246371e
--- /dev/null
+++ b/lucene/facet/build.gradle
@@ -0,0 +1,12 @@
+
+apply plugin: 'java-library'
+
+
+dependencies { 
+  api project(':lucene:core')
+
+  implementation 'com.carrotsearch:hppc'
+
+  testImplementation project(':lucene:test-framework')
+  testImplementation project(':lucene:queries')
+}
diff --git a/lucene/grouping/build.gradle b/lucene/grouping/build.gradle
new file mode 100644
index 0000000..ab0e891
--- /dev/null
+++ b/lucene/grouping/build.gradle
@@ -0,0 +1,10 @@
+
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+
+  implementation project(':lucene:queries')
+
+  testImplementation project(':lucene:test-framework')
+}
\ No newline at end of file
diff --git a/lucene/highlighter/build.gradle b/lucene/highlighter/build.gradle
new file mode 100644
index 0000000..c46636d
--- /dev/null
+++ b/lucene/highlighter/build.gradle
@@ -0,0 +1,12 @@
+
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+
+  implementation project(':lucene:queries')
+  implementation project(':lucene:memory')
+
+  testImplementation project(':lucene:test-framework')
+  testImplementation project(':lucene:analysis:common')
+}
diff --git a/lucene/join/build.gradle b/lucene/join/build.gradle
new file mode 100644
index 0000000..fa8aca9
--- /dev/null
+++ b/lucene/join/build.gradle
@@ -0,0 +1,6 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  testImplementation project(':lucene:test-framework')
+}
\ No newline at end of file
diff --git a/lucene/luke/build.gradle b/lucene/luke/build.gradle
new file mode 100644
index 0000000..4e9fc42
--- /dev/null
+++ b/lucene/luke/build.gradle
@@ -0,0 +1,16 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+
+  implementation project(':lucene:codecs')
+  implementation project(':lucene:backward-codecs')
+  implementation project(':lucene:analysis:common')
+  implementation project(':lucene:queries')
+  implementation project(':lucene:queryparser')
+  implementation project(':lucene:misc')
+
+  implementation 'org.apache.logging.log4j:log4j-core'
+
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/memory/build.gradle b/lucene/memory/build.gradle
new file mode 100644
index 0000000..92b4c5b
--- /dev/null
+++ b/lucene/memory/build.gradle
@@ -0,0 +1,9 @@
+
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+
+  testImplementation project(':lucene:test-framework')
+  testImplementation project(':lucene:queryparser')
+}
\ No newline at end of file
diff --git a/lucene/misc/build.gradle b/lucene/misc/build.gradle
new file mode 100644
index 0000000..fa8aca9
--- /dev/null
+++ b/lucene/misc/build.gradle
@@ -0,0 +1,6 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  testImplementation project(':lucene:test-framework')
+}
\ No newline at end of file
diff --git a/lucene/monitor/build.gradle b/lucene/monitor/build.gradle
new file mode 100644
index 0000000..fc9583a
--- /dev/null
+++ b/lucene/monitor/build.gradle
@@ -0,0 +1,11 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+
+  implementation project(':lucene:memory')
+  implementation project(':lucene:analysis:common')
+
+  testImplementation project(':lucene:queryparser')
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/queries/build.gradle b/lucene/queries/build.gradle
new file mode 100644
index 0000000..6212520
--- /dev/null
+++ b/lucene/queries/build.gradle
@@ -0,0 +1,8 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+
+  testImplementation project(':lucene:test-framework')
+  testImplementation project(':lucene:expressions')
+}
\ No newline at end of file
diff --git a/lucene/queryparser/build.gradle b/lucene/queryparser/build.gradle
new file mode 100644
index 0000000..1a562c4
--- /dev/null
+++ b/lucene/queryparser/build.gradle
@@ -0,0 +1,9 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  api project(':lucene:queries')
+  api project(':lucene:sandbox')
+
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/replicator/build.gradle b/lucene/replicator/build.gradle
new file mode 100644
index 0000000..f924533
--- /dev/null
+++ b/lucene/replicator/build.gradle
@@ -0,0 +1,19 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+
+  implementation project(':lucene:facet')
+
+  implementation('org.apache.httpcomponents:httpclient', {
+    exclude group: "commons-codec", module: "commons-codec"
+  })
+
+  implementation 'org.eclipse.jetty:jetty-server'
+  implementation('org.eclipse.jetty:jetty-servlet', {
+    exclude group: "org.eclipse.jetty", module: "jetty-security"
+  })
+  implementation 'org.eclipse.jetty:jetty-continuation'
+
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/sandbox/build.gradle b/lucene/sandbox/build.gradle
new file mode 100644
index 0000000..f59fd2c
--- /dev/null
+++ b/lucene/sandbox/build.gradle
@@ -0,0 +1,6 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/spatial-extras/build.gradle b/lucene/spatial-extras/build.gradle
new file mode 100644
index 0000000..444148f
--- /dev/null
+++ b/lucene/spatial-extras/build.gradle
@@ -0,0 +1,14 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  api project(':lucene:spatial3d')
+
+  api 'org.locationtech.spatial4j:spatial4j'
+  api 'io.sgr:s2-geometry-library-java'
+
+  testImplementation project(':lucene:test-framework')
+
+  testImplementation 'org.locationtech.jts:jts-core'
+  testImplementation 'org.locationtech.spatial4j:spatial4j::tests'
+}
diff --git a/lucene/spatial/build.gradle b/lucene/spatial/build.gradle
new file mode 100644
index 0000000..fa8aca9
--- /dev/null
+++ b/lucene/spatial/build.gradle
@@ -0,0 +1,6 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  testImplementation project(':lucene:test-framework')
+}
\ No newline at end of file
diff --git a/lucene/spatial3d/build.gradle b/lucene/spatial3d/build.gradle
new file mode 100644
index 0000000..fa8aca9
--- /dev/null
+++ b/lucene/spatial3d/build.gradle
@@ -0,0 +1,6 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  testImplementation project(':lucene:test-framework')
+}
\ No newline at end of file
diff --git a/lucene/suggest/build.gradle b/lucene/suggest/build.gradle
new file mode 100644
index 0000000..f31a53e
--- /dev/null
+++ b/lucene/suggest/build.gradle
@@ -0,0 +1,8 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  api project(':lucene:analysis:common')
+  
+  testImplementation project(':lucene:test-framework')
+}
diff --git a/lucene/test-framework/build.gradle b/lucene/test-framework/build.gradle
new file mode 100644
index 0000000..c0b7615
--- /dev/null
+++ b/lucene/test-framework/build.gradle
@@ -0,0 +1,11 @@
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+
+  api ("com.carrotsearch.randomizedtesting:randomizedtesting-runner")
+  api ('org.hamcrest:hamcrest-core')
+  api ("junit:junit")
+
+  implementation project(':lucene:codecs')
+}
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..a04620d
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,55 @@
+
+include "lucene:analysis:common"
+include "lucene:analysis:icu"
+include "lucene:analysis:kuromoji"
+include "lucene:analysis:morfologik"
+include "lucene:analysis:nori"
+include "lucene:analysis:opennlp"
+include "lucene:analysis:phonetic"
+include "lucene:analysis:smartcn"
+include "lucene:analysis:stempel"
+include "lucene:backward-codecs"
+include "lucene:benchmark"
+include "lucene:classification"
+include "lucene:codecs"
+include "lucene:core"
+include "lucene:demo"
+include "lucene:expressions"
+include "lucene:facet"
+include "lucene:grouping"
+include "lucene:highlighter"
+include "lucene:join"
+include "lucene:luke"
+include "lucene:memory"
+include "lucene:misc"
+include "lucene:monitor"
+include "lucene:queries"
+include "lucene:queryparser"
+include "lucene:replicator"
+include "lucene:sandbox"
+include "lucene:spatial"
+include "lucene:spatial-extras"
+include "lucene:spatial3d"
+include "lucene:suggest"
+include "lucene:test-framework"
+
+include "solr:solrj"
+include "solr:core"
+include "solr:server"
+include "solr:contrib:analysis-extras"
+include "solr:contrib:dataimporthandler"
+include "solr:contrib:dataimporthandler-extras"
+include "solr:contrib:analytics"
+include "solr:contrib:clustering"
+include "solr:contrib:extraction"
+include "solr:contrib:langid"
+include "solr:contrib:jaegertracer-configurator"
+include "solr:contrib:prometheus-exporter"
+include "solr:contrib:velocity"
+include "solr:contrib:ltr"
+include "solr:webapp"
+include "solr:test-framework"
+include "solr:solr-ref-guide"
+include "solr:example"
+
+include "solr:packaging"
diff --git a/solr/build.gradle b/solr/build.gradle
new file mode 100644
index 0000000..e48b30f
--- /dev/null
+++ b/solr/build.gradle
@@ -0,0 +1,3 @@
+subprojects {
+  group "org.apache.solr"
+}
\ No newline at end of file
diff --git a/solr/contrib/analysis-extras/build.gradle b/solr/contrib/analysis-extras/build.gradle
new file mode 100644
index 0000000..8e6ab06
--- /dev/null
+++ b/solr/contrib/analysis-extras/build.gradle
@@ -0,0 +1,16 @@
+
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':solr:core')
+
+  implementation project(':lucene:analysis:icu')
+  implementation project(':lucene:analysis:smartcn')
+  implementation project(':lucene:analysis:morfologik')
+  implementation project(':lucene:analysis:opennlp')
+  implementation project(':lucene:analysis:smartcn')
+  implementation project(':lucene:analysis:stempel')
+
+  testImplementation project(':solr:test-framework')
+}
+
diff --git a/solr/contrib/analytics/build.gradle b/solr/contrib/analytics/build.gradle
new file mode 100644
index 0000000..de21ff6
--- /dev/null
+++ b/solr/contrib/analytics/build.gradle
@@ -0,0 +1,6 @@
+apply plugin: 'java-library'
+
+dependencies {
+  implementation project(':solr:core')
+  testImplementation project(':solr:test-framework')
+}
diff --git a/solr/contrib/clustering/build.gradle b/solr/contrib/clustering/build.gradle
new file mode 100644
index 0000000..860a645
--- /dev/null
+++ b/solr/contrib/clustering/build.gradle
@@ -0,0 +1,12 @@
+
+apply plugin: 'java-library'
+
+dependencies {
+  implementation project(':solr:core')
+  implementation project(':lucene:analysis:common')
+  implementation('org.carrot2:carrot2-mini', {
+    exclude group: "org.simpleframework", module: "simple-xml"
+  })
+
+  testImplementation project(':solr:test-framework')
+}
diff --git a/solr/contrib/dataimporthandler-extras/build.gradle b/solr/contrib/dataimporthandler-extras/build.gradle
new file mode 100644
index 0000000..bbf4c69
--- /dev/null
+++ b/solr/contrib/dataimporthandler-extras/build.gradle
@@ -0,0 +1,14 @@
+apply plugin: 'java-library'
+
+dependencies {
+  implementation project(':solr:core')
+
+  implementation project(':solr:contrib:dataimporthandler')
+  implementation project(':solr:contrib:extraction')
+
+  implementation ('javax.activation:activation')
+  implementation ('com.sun.mail:javax.mail')
+  implementation ('com.sun.mail:gimap')
+
+  testImplementation project(':solr:test-framework')
+}
diff --git a/solr/contrib/dataimporthandler/build.gradle b/solr/contrib/dataimporthandler/build.gradle
new file mode 100644
index 0000000..d34b9dd
--- /dev/null
+++ b/solr/contrib/dataimporthandler/build.gradle
@@ -0,0 +1,15 @@
+
+apply plugin: 'java-library'
+
+dependencies {
+  implementation project(':solr:core')
+
+  testImplementation project(':solr:test-framework')
+
+  testImplementation('org.mockito:mockito-core', {
+    exclude group: "net.bytebuddy", module: "byte-buddy-agent"
+  })
+  testImplementation ('org.hsqldb:hsqldb')
+  testImplementation ('org.apache.derby:derby')
+  testImplementation ('org.objenesis:objenesis')
+}
diff --git a/solr/contrib/extraction/build.gradle b/solr/contrib/extraction/build.gradle
new file mode 100644
index 0000000..7875efc
--- /dev/null
+++ b/solr/contrib/extraction/build.gradle
@@ -0,0 +1,55 @@
+apply plugin: 'java-library'
+
+dependencies {
+  implementation project(':solr:core')
+
+  // We export tika because other contribs depend on it (and its submodules)
+  // and we don't want to duplicate the dependency across different contribs.
+  api ('org.apache.tika:tika-core') { transitive = false }
+  api ('org.apache.tika:tika-parsers') { transitive = false }
+
+  runtimeOnly ('org.apache.james:apache-mime4j-core') { transitive = false }
+  runtimeOnly ('org.apache.james:apache-mime4j-dom') { transitive = false }
+  runtimeOnly ('org.apache.tika:tika-java7') { transitive = false }
+  runtimeOnly ('org.apache.tika:tika-xmp') { transitive = false }
+
+  implementation ('com.healthmarketscience.jackcess:jackcess') { transitive = false }
+  implementation ('com.healthmarketscience.jackcess:jackcess-encrypt') { transitive = false }
+  implementation ('org.gagravarr:vorbis-java-tika') { transitive = false }
+  implementation ('org.gagravarr:vorbis-java-core') { transitive = false }
+  implementation ('org.apache.commons:commons-compress') { transitive = false }
+  implementation ('org.apache.pdfbox:pdfbox') { transitive = false }
+  implementation ('org.apache.pdfbox:pdfbox-tools') { transitive = false }
+  implementation ('org.apache.pdfbox:fontbox') { transitive = false }
+  implementation ('org.apache.pdfbox:jempbox') { transitive = false }
+  implementation ('org.bouncycastle:bcmail-jdk15on') { transitive = false }
+  implementation ('org.bouncycastle:bcpkix-jdk15on') { transitive = false }
+  implementation ('org.bouncycastle:bcprov-jdk15on') { transitive = false }
+  implementation ('org.apache.poi:poi') { transitive = false }
+  implementation ('org.apache.poi:poi-scratchpad') { transitive = false }
+  implementation ('org.apache.poi:poi-ooxml') { transitive = false }
+  implementation ('org.apache.poi:poi-ooxml-schemas') { transitive = false }
+  implementation ('org.apache.xmlbeans:xmlbeans') { transitive = false }
+  implementation ('org.apache.commons:commons-collections4') { transitive = false }
+  implementation ('com.github.virtuald:curvesapi') { transitive = false }
+  implementation ('org.ccil.cowan.tagsoup:tagsoup') { transitive = false }
+  implementation ('com.googlecode.mp4parser:isoparser') { transitive = false }
+  implementation ('org.aspectj:aspectjrt') { transitive = false }
+  implementation ('com.drewnoakes:metadata-extractor') { transitive = false }
+  implementation ('de.l3s.boilerpipe:boilerpipe') { transitive = false }
+  implementation ('com.rometools:rome') { transitive = false }
+  implementation ('com.rometools:rome-utils') { transitive = false }
+  implementation ('org.jdom:jdom2') { transitive = false }
+  implementation ('com.googlecode.juniversalchardet:juniversalchardet') { transitive = false }
+  implementation ('org.tukaani:xz') { transitive = false }
+  implementation ('com.adobe.xmp:xmpcore') { transitive = false }
+  implementation ('com.pff:java-libpst') { transitive = false }
+  implementation ('org.tallison:jmatio') { transitive = false }
+  implementation ('com.epam:parso') { transitive = false }
+  implementation ('org.brotli:dec') { transitive = false }
+  implementation ('xerces:xercesImpl') { transitive = false }
+
+  implementation ('com.ibm.icu:icu4j')
+
+  testImplementation project(':solr:test-framework')
+}
diff --git a/solr/contrib/jaegertracer-configurator/build.gradle b/solr/contrib/jaegertracer-configurator/build.gradle
new file mode 100644
index 0000000..f8e13a8
--- /dev/null
+++ b/solr/contrib/jaegertracer-configurator/build.gradle
@@ -0,0 +1,12 @@
+apply plugin: 'java-library'
+
+dependencies {
+  implementation project(':solr:core')
+
+  implementation ("io.jaegertracing:jaeger-thrift", {
+    exclude group: "com.squareup.okhttp3", module: "okhttp"
+    exclude group: "com.google.code.gson", module: "gson"
+  })
+
+  testImplementation project(':solr:test-framework')
+}
\ No newline at end of file
diff --git a/solr/contrib/langid/build.gradle b/solr/contrib/langid/build.gradle
new file mode 100644
index 0000000..6621dd0
--- /dev/null
+++ b/solr/contrib/langid/build.gradle
@@ -0,0 +1,15 @@
+
+apply plugin: 'java-library'
+
+dependencies {
+  implementation project(':solr:core')
+
+  implementation ('org.apache.tika:tika-core') { transitive = false }
+
+  implementation 'com.cybozu.labs:langdetect'
+  implementation 'net.arnx:jsonic'
+  implementation 'org.apache.opennlp:opennlp-tools'
+
+
+  testImplementation project(':solr:test-framework')
+}
diff --git a/solr/contrib/ltr/build.gradle b/solr/contrib/ltr/build.gradle
new file mode 100644
index 0000000..638ca38
--- /dev/null
+++ b/solr/contrib/ltr/build.gradle
@@ -0,0 +1,14 @@
+apply plugin: 'java-library'
+
+dependencies {
+  implementation project(':solr:core')
+  implementation project(':lucene:analysis:common')
+
+  testImplementation('org.mockito:mockito-core', {
+    exclude group: "net.bytebuddy", module: "byte-buddy-agent"
+  })
+  testImplementation ('org.objenesis:objenesis')
+  testImplementation ('org.restlet.jee:org.restlet.ext.servlet')
+
+  testImplementation project(':solr:test-framework')
+}
diff --git a/solr/contrib/prometheus-exporter/build.gradle b/solr/contrib/prometheus-exporter/build.gradle
new file mode 100644
index 0000000..e01b42b
--- /dev/null
+++ b/solr/contrib/prometheus-exporter/build.gradle
@@ -0,0 +1,28 @@
+
+apply plugin: 'java-library'
+
+dependencies {
+  implementation project(':solr:core')
+  implementation project(':lucene:analysis:common')
+
+  implementation ('io.prometheus:simpleclient')
+  implementation ('io.prometheus:simpleclient_common')
+  implementation ('io.prometheus:simpleclient_httpserver')
+  implementation ('net.thisptr:jackson-jq', {
+    exclude group: "org.jruby.joni", module: "joni"
+  })
+  implementation ('net.sourceforge.argparse4j:argparse4j')
+
+  testImplementation ('org.apache.httpcomponents:httpcore')
+  testImplementation ('org.eclipse.jetty:jetty-servlet')
+
+  testImplementation project(':solr:test-framework')
+}
+
+// Add two folders to default packaging.
+assemblePackaging {
+  from(projectDir, {
+    include "bin/**"
+    include "conf/**"
+  })
+}
\ No newline at end of file
diff --git a/solr/contrib/velocity/build.gradle b/solr/contrib/velocity/build.gradle
new file mode 100644
index 0000000..2163614
--- /dev/null
+++ b/solr/contrib/velocity/build.gradle
@@ -0,0 +1,14 @@
+
+apply plugin: 'java-library'
+
+dependencies {
+  implementation project(':solr:core')
+
+  implementation('org.apache.velocity.tools:velocity-tools-view-jsp', {
+    exclude group: "commons-beanutils", module: "commons-beanutils"
+    exclude group: "org.apache.commons", module: "commons-digester3"
+    exclude group: "com.github.cliftonlabs", module: "json-simple"
+  })
+
+  testImplementation project(':solr:test-framework')
+}
diff --git a/solr/core/build.gradle b/solr/core/build.gradle
new file mode 100644
index 0000000..0f0a4d0
--- /dev/null
+++ b/solr/core/build.gradle
@@ -0,0 +1,129 @@
+
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':lucene:core')
+  api project(':lucene:analysis:common')
+  api project(':lucene:analysis:kuromoji')
+  api project(':lucene:analysis:nori')
+  api project(':lucene:analysis:phonetic')
+  api project(':lucene:backward-codecs')
+  api project(':lucene:classification')
+  api project(':lucene:codecs')
+  api project(':lucene:expressions')
+  api project(':lucene:grouping')
+  api project(':lucene:highlighter')
+  api project(':lucene:join')
+  api project(':lucene:misc')
+  api project(':lucene:queries')
+  api project(':lucene:queryparser')
+  api project(':lucene:sandbox')
+  api project(':lucene:spatial-extras')
+  api project(':lucene:suggest')
+
+  // Export these dependencies so that they're imported transitively by
+  // other modules.
+  api ('com.google.guava:guava', {
+    exclude group: "org.codehaus.mojo", module: "animal-sniffer-annotations"
+    exclude group: "com.google.j2objc", module: "j2objc-annotations"
+    exclude group: "com.google.errorprone", module: "error_prone_annotations"
+    exclude group: "org.checkerframework", module: "checker-qual"
+    exclude group: "com.google.code.findbugs", module: "jsr305"
+  })
+
+  api project(':solr:solrj')
+  api project(':solr:server')
+
+  api 'org.apache.commons:commons-lang3'
+  api 'com.carrotsearch:hppc'
+  api 'com.fasterxml.jackson.core:jackson-databind'
+  api 'commons-cli:commons-cli'
+  api 'commons-codec:commons-codec'
+  api 'commons-collections:commons-collections'
+
+  implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-smile'
+
+  implementation('com.github.ben-manes.caffeine:caffeine', {
+    exclude group: "org.checkerframework", module: "checker-qual"
+    exclude group: "com.google.errorprone", module: "error_prone_annotations"
+  })
+
+  implementation 'com.github.zafarkhaja:java-semver'
+  implementation 'com.google.re2j:re2j'
+
+  implementation('com.jayway.jsonpath:json-path', {
+    exclude group: "net.minidev", module: "json-smart"
+  })
+
+  implementation 'com.tdunning:t-digest'
+  implementation 'commons-fileupload:commons-fileupload'
+
+  implementation 'io.opentracing:opentracing-api'
+  implementation 'io.opentracing:opentracing-noop'
+  implementation 'io.opentracing:opentracing-util'
+  implementation 'org.apache.commons:commons-exec'
+  implementation 'org.apache.commons:commons-text'
+  implementation("org.apache.commons:commons-configuration2", {
+    exclude group: "commons-logging", module: "commons-logging"
+  })
+  implementation 'org.apache.htrace:htrace-core4'
+
+  implementation 'org.apache.logging.log4j:log4j-api'
+  implementation 'org.apache.logging.log4j:log4j-core'
+  implementation 'org.apache.logging.log4j:log4j-slf4j-impl'
+
+  api 'org.bitbucket.b_c:jose4j'
+  implementation 'org.codehaus.janino:commons-compiler'
+  implementation 'org.codehaus.janino:janino'
+
+  api 'org.restlet.jee:org.restlet'
+  implementation 'org.rrd4j:rrd4j'
+  implementation 'org.restlet.jee:org.restlet.ext.servlet'
+
+  implementation ('org.apache.calcite.avatica:avatica-core') { transitive = false }
+  implementation ('org.apache.calcite:calcite-core') { transitive = false }
+  implementation ('org.apache.calcite:calcite-linq4j') { transitive = false }
+  implementation ('org.apache.curator:curator-client') { transitive = false }
+  implementation ('org.apache.curator:curator-framework') { transitive = false }
+  implementation ('org.apache.hadoop:hadoop-annotations') { transitive = false }
+  implementation ('org.apache.hadoop:hadoop-auth') { transitive = false }
+  implementation ('org.apache.hadoop:hadoop-common') { transitive = false }
+  implementation ('org.apache.hadoop:hadoop-hdfs-client') { transitive = false }
+
+  implementation ('net.hydromatic:eigenbase-properties') { transitive = false }
+
+  runtimeOnly ('org.apache.curator:curator-recipes') { transitive = false }
+  runtimeOnly ('org.apache.kerby:kerb-core')
+  runtimeOnly ('org.apache.kerby:kerb-util')
+  runtimeOnly ('org.apache.kerby:kerby-asn1')
+  runtimeOnly ('org.apache.kerby:kerby-pkix')
+  runtimeOnly ('com.google.protobuf:protobuf-java')
+
+  testImplementation project(':lucene:analysis:icu')
+  testImplementation project(':solr:contrib:analysis-extras')
+  testImplementation project(':solr:test-framework')
+
+  testImplementation ('org.apache.hadoop:hadoop-common::tests') { transitive = false }
+  testImplementation ('org.apache.hadoop:hadoop-hdfs') { transitive = false }
+  testImplementation ('org.apache.hadoop:hadoop-hdfs::tests') { transitive = false }
+  testImplementation ('org.apache.hadoop:hadoop-minikdc') { transitive = false }
+
+  testImplementation ('org.apache.kerby:kerb-client') { transitive = false }
+  testImplementation ('org.apache.kerby:kerb-common') { transitive = false }
+  testImplementation ('org.apache.kerby:kerb-identity') { transitive = false }
+  testImplementation ('org.apache.kerby:kerb-server') { transitive = false }
+  testImplementation ('org.apache.kerby:kerb-simplekdc') { transitive = false }
+  testImplementation ('org.apache.kerby:kerb-admin') { transitive = false }
+  testImplementation ('org.apache.kerby:kerby-kdc') { transitive = false }
+
+  testImplementation ('com.sun.jersey:jersey-servlet:1.19.4') { transitive = false }
+
+  testImplementation 'com.google.protobuf:protobuf-java'
+  testImplementation 'commons-logging:commons-logging'
+  testImplementation('org.mockito:mockito-core', {
+    exclude group: "net.bytebuddy", module: "byte-buddy-agent"
+  })
+
+  testRuntimeOnly 'org.slf4j:log4j-over-slf4j'
+}
+
diff --git a/solr/example/build.gradle b/solr/example/build.gradle
new file mode 100644
index 0000000..e1a5314
--- /dev/null
+++ b/solr/example/build.gradle
@@ -0,0 +1,46 @@
+
+// I am not convinced packaging of examples should be a separate project... Seems more logical to
+// move it to just the packaging project (?). Let's leave it for now though.
+
+configurations {
+  packaging
+  postJar
+  dih
+}
+
+dependencies {
+  postJar project(path: ":solr:core", configuration: "postJar")
+  dih 'org.hsqldb:hsqldb'
+  dih 'org.apache.derby:derby'
+}
+
+ext {
+  packagingDir = file("${buildDir}/packaging")
+}
+
+task assemblePackaging(type: Sync) {
+  from(projectDir, {
+    include "example-DIH/**"
+    include "exampledocs/**"
+    include "files/**"
+    include "films/**"
+    include "README.txt"
+    exclude "**/*.jar"
+  })
+
+  from(configurations.postJar, {
+    into "exampledocs/"
+  })
+
+  from(configurations.dih, {
+    into "example-DIH/solr/db/lib"
+  })
+
+  into packagingDir
+}
+
+artifacts {
+  packaging packagingDir, {
+    builtBy assemblePackaging
+  }
+}
diff --git a/solr/packaging/build.gradle b/solr/packaging/build.gradle
new file mode 100644
index 0000000..57b1572
--- /dev/null
+++ b/solr/packaging/build.gradle
@@ -0,0 +1,95 @@
+
+// This project puts together a "distribution", assembling dependencies from
+// various other projects.
+
+plugins {
+  id 'base'
+}
+
+ext {
+  distDir = file("$buildDir/solr-${version}")
+}
+
+configurations {
+  distSolr {
+    transitive = false
+  }
+  distSolrj
+  contrib
+  example
+  server
+}
+
+dependencies {
+  distSolrj project(":solr:solrj")
+
+  [":solr:contrib:analysis-extras",
+   ":solr:contrib:analytics",
+   ":solr:contrib:extraction",
+   ":solr:contrib:clustering",
+   ":solr:contrib:dataimporthandler",
+   ":solr:contrib:dataimporthandler-extras",
+   ":solr:contrib:jaegertracer-configurator",
+   ":solr:contrib:langid",
+   ":solr:contrib:ltr",
+   ":solr:contrib:prometheus-exporter",
+   ":solr:contrib:velocity",
+  ].each { contribName ->
+    distSolr project(contribName)
+    contrib  project(path: contribName, configuration: "packaging")
+  }
+
+  distSolr project(":solr:core")
+  distSolr project(":solr:solrj")
+  distSolr project(":solr:test-framework")
+
+  example project(path: ":solr:example", configuration: "packaging")
+  server project(path: ":solr:server", configuration: "packaging")
+}
+
+task toDir(type: Sync) {
+  from(project(":solr").projectDir, {
+    include "bin/**"
+    include "licenses/**"
+    include "CHANGES.txt"
+    include "LICENSE.txt"
+    include "NOTICE.txt"
+    include "README.txt"
+  })
+
+  from(project(":lucene").projectDir, {
+    include "CHANGES.txt"
+    rename { file -> 'LUCENE_CHANGES.txt' }
+  })
+
+  from(configurations.contrib, {
+    into "contrib"
+  })
+
+  from(configurations.distSolr, {
+    into "dist"
+  })
+
+  from(configurations.distSolrj - configurations.distSolr, {
+    into "dist/solrj-lib"
+  })
+
+  from(configurations.example, {
+    into "example"
+  })
+
+  from(configurations.server, {
+    into "server"
+  })
+
+  // docs/   - TODO: this is assembled via XSLT... leaving out for now.
+
+  into distDir
+
+  doLast {
+    logger.lifecycle "Solr distribution assembled under: ${distDir}"
+  }
+}
+
+assemble.dependsOn toDir
+
diff --git a/solr/server/build.gradle b/solr/server/build.gradle
new file mode 100644
index 0000000..fd0e880
--- /dev/null
+++ b/solr/server/build.gradle
@@ -0,0 +1,107 @@
+apply plugin: 'java-library'
+
+configurations {
+  api {
+    exclude group: "org.slf4j"
+  }
+  startJar
+  libExt
+  webapp
+  packaging
+}
+
+dependencies {
+  api('org.eclipse.jetty:jetty-alpn-java-server', {
+    exclude group: "org.eclipse.jetty.alpn", module: "alpn-api"
+  })
+
+  api('io.dropwizard.metrics:metrics-core', {
+    exclude group: "com.rabbitmq", module: "amqp-client"
+  })
+  api('io.dropwizard.metrics:metrics-graphite', {
+    exclude group: "com.rabbitmq", module: "amqp-client"
+  })
+  api 'io.dropwizard.metrics:metrics-jetty9'
+  api 'io.dropwizard.metrics:metrics-jvm'
+  api 'io.dropwizard.metrics:metrics-jmx'
+
+  api 'org.eclipse.jetty:jetty-continuation'
+  api 'org.eclipse.jetty:jetty-deploy'
+  api 'org.eclipse.jetty:jetty-http'
+  api 'org.eclipse.jetty:jetty-io'
+  api 'org.eclipse.jetty:jetty-jmx'
+  api 'org.eclipse.jetty:jetty-rewrite'
+  api 'org.eclipse.jetty:jetty-security'
+  api 'org.eclipse.jetty:jetty-server'
+  api 'org.eclipse.jetty:jetty-servlet'
+  api 'org.eclipse.jetty:jetty-servlets'
+  api 'org.eclipse.jetty:jetty-util'
+  api 'org.eclipse.jetty:jetty-webapp'
+  api 'org.eclipse.jetty:jetty-xml'
+  api 'org.eclipse.jetty:jetty-alpn-server'
+
+  api 'org.eclipse.jetty.http2:http2-server'
+  api 'org.eclipse.jetty.http2:http2-common'
+  api 'org.eclipse.jetty.http2:http2-hpack'
+
+  api 'javax.servlet:javax.servlet-api'
+
+  libExt 'com.lmax:disruptor'
+  libExt 'org.slf4j:jcl-over-slf4j'
+  libExt 'org.slf4j:jul-to-slf4j'
+  libExt 'org.slf4j:slf4j-api'
+  libExt 'org.apache.logging.log4j:log4j-1.2-api'
+  libExt 'org.apache.logging.log4j:log4j-api'
+  libExt 'org.apache.logging.log4j:log4j-core'
+  libExt 'org.apache.logging.log4j:log4j-slf4j-impl'
+  libExt 'org.apache.logging.log4j:log4j-web'
+
+  webapp project(path: ":solr:webapp", configuration: "war")
+
+  startJar('org.eclipse.jetty:jetty-start::shaded', {
+    transitive false
+  })
+}
+
+ext {
+  packagingDir = file("${buildDir}/packaging")
+}
+
+task assemblePackaging(type: Sync) {
+  from(projectDir, {
+    include "contexts/**"
+    include "etc/**"
+    include "modules/**"
+    include "resources/**"
+    include "scripts/**"
+    include "solr/**"
+    include "README.txt"
+  })
+
+  from(configurations.compileClasspath, {
+    into "lib/"
+  })
+
+  from(configurations.libExt, {
+    into "lib/ext"
+  })
+
+  from { project.configurations.startJar.singleFile } {
+    rename { file -> 'start.jar' }
+  }
+
+  dependsOn configurations.webapp
+  from( { zipTree(configurations.webapp.asPath) }, {
+    into "solr-webapp/webapp"
+  })
+
+  into packagingDir
+}
+
+artifacts {
+  packaging packagingDir, {
+    builtBy assemblePackaging
+  }
+}
+
+assemble.dependsOn assemblePackaging
diff --git a/solr/solr-ref-guide/build.gradle b/solr/solr-ref-guide/build.gradle
new file mode 100644
index 0000000..74f3d07
--- /dev/null
+++ b/solr/solr-ref-guide/build.gradle
@@ -0,0 +1,328 @@
+// TODO 1: the separation of sources between tools and refGuide is awkward; it'd be
+// better to separate the refGuideTools as a plain Java module and then depend on
+// it as a project dependency. This would enable this module to *not* be a java module at all
+// and inherit from base, adding just refGuide-related tasks.
+// OR (better) one could rewrite those tools in Groovy (or Kotlin) and use them directly, without
+// an additional compilation phase.
+
+// TODO 2: property expansion via ant properties is awkward in gradle. We can do cleaner than going
+// through ant -- we could use gradle's expand when copying or at least use some more humane
+// property names.
+
+// TODO 3: task names currently roughly correspond to ant tasks. Simpler aliases (such as 'html')
+// would be much clearer.
+
+// TODO 4: currently buildscript dependencies are hardcoded (asciidoctor) because they can't be resolved
+// using Palantir's plugin. This is another reason to switch to gradle-based tools -- then
+// only the build script dependencies would be needed and asciidoctorj would be removed from version
+// properties entirely (it is only used locally in this build file).
+
+import java.time.*
+import java.time.format.*
+import java.nio.file.*
+import org.asciidoctor.*
+
+buildscript {
+    repositories {
+        mavenCentral()
+    }
+
+    dependencies {
+        classpath "org.asciidoctor:asciidoctorj:1.6.2"
+    }
+}
+
+plugins {
+    id 'java'
+    id 'com.github.jruby-gradle.base' version '1.6.0'
+}
+
+configurations {
+    depVer
+    refGuide
+}
+
+dependencies {
+    // Dependencies to compile internal tools.
+    implementation('org.asciidoctor:asciidoctorj')
+    implementation('com.vaadin.external.google:android-json')
+    implementation('org.jsoup:jsoup')
+    implementation('org.slf4j:jcl-over-slf4j')
+    implementation('org.slf4j:slf4j-simple')
+    implementation('org.apache.logging.log4j:log4j-core')
+    implementation('com.google.guava:guava')
+    implementation('commons-codec:commons-codec')
+
+    // Dependencies referenced in the guide.
+    depVer('commons-codec:commons-codec')
+    depVer('io.dropwizard.metrics:metrics-core')
+    depVer('org.apache.logging.log4j:log4j-core')
+    depVer('org.apache.opennlp:opennlp-tools')
+    depVer('org.apache.tika:tika-core')
+    depVer('org.apache.velocity.tools:velocity-tools-generic')
+    depVer('org.apache.zookeeper:zookeeper')
+
+    // jekyll dependencies
+    jrubyExec 'rubygems:jekyll:3.5.2'
+    jrubyExec 'rubygems:jekyll-asciidoc:3.0.0'
+
+    // don't know why we have to explicitly add these deps but it doesn't resolve them
+    // automatically.
+    jrubyExec 'rubygems:tilt:2.0.10'
+    jrubyExec 'rubygems:slim:4.0.1'
+    jrubyExec 'rubygems:concurrent-ruby:1.0.5'
+}
+
+sourceSets {
+    refGuide {
+        java {
+            srcDirs = ['src']
+        }
+    }
+
+    main {
+        java {
+            srcDirs = ['tools']
+            exclude "**/CustomizedAsciidoctorAntTask.java"
+            exclude "**/asciidoctor-antlib.xml"
+        }
+    }
+}
+
+ext {
+    buildContentDir = file("${buildDir}/content")
+    mainPage = "index"
+
+    solrDocsVersion = "${version}".replaceAll(/^(\d+\.\d+)(|\..*)$/, "\$1")
+    solrDocsVersionPath = "${solrDocsVersion}".replaceAll(/^(\d+)\.(\d+)$/, "\$1_\$2_0")
+
+    if (!project.hasProperty("solrGuideVersion")) {
+        solrGuideVersion = "${solrDocsVersion}-DRAFT"
+    }
+
+    solrRootPath = '../../../../solr/'
+    solrGuideDraftStatus = solrGuideVersion.matches(/^\d+\.\d+(|\.\d+)$/) ? "" : "DRAFT"
+    solrGuideVersionPath = solrGuideVersion.replaceAll(/^(\d+)\.(\d+)(-DRAFT)?.*/, "\$1_\$2\$3")
+
+    javadocLink = "https://docs.oracle.com/en/java/javase/11/docs/api/"
+
+    if (project.hasProperty("local.javadocs")) {
+        htmlSolrJavadocs = "link:../../docs/"
+        htmlLuceneJavadocs = "link:../../../../lucene/build/docs/"
+    } else {
+        htmlSolrJavadocs = "https://lucene.apache.org/solr/${solrDocsVersionPath}/"
+        htmlLuceneJavadocs = "https://lucene.apache.org/core/${solrDocsVersionPath}/"
+    }
+
+    bareBonesDir = file("${buildDir}/bare-bones-html")
+
+    def tstamp = ZonedDateTime.now()
+    buildDate = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(tstamp)
+    buildTime = DateTimeFormatter.ofPattern("HH:mm:ss").format(tstamp)
+    buildYear = DateTimeFormatter.ofPattern("yyyy").format(tstamp)
+
+    // Some additional version properties are lazily computed
+    // in setupProps (because they need to be computed after evalution is complete).
+    props = [
+            "solr-root-path"         : solrRootPath,
+            "solr-guide-draft-status": solrGuideDraftStatus,
+            "solr-guide-version"     : solrGuideVersion,
+            "solr-guide-version-path": solrGuideVersionPath,
+            "solr-docs-version"      : solrDocsVersion,
+            "javadoc.link"           : javadocLink,
+            "java-javadocs"          : javadocLink,
+            "solr-javadocs"          : htmlSolrJavadocs,
+            "html-solr-javadocs"     : htmlSolrJavadocs,
+            "lucene-javadocs"        : htmlLuceneJavadocs,
+            "html-lucene-javadocs"   : htmlLuceneJavadocs,
+            "build-date"             : buildDate,
+            "DSTAMP"                 : buildDate,
+            "build-year"             : buildYear,
+            "current.year"           : buildYear
+    ]
+
+    asciiDocAttrs = [
+            'common': [
+                    'attribute-missing' : 'warn',
+                    'section-toc'       : '',
+                    'icons'             : 'font',
+                    'icon-set'          : 'fa',
+                    'figure-caption!'   : '',
+                    'idprefix'          : '',
+                    'idseparator'       : '-',
+                    'source-highlighter': 'coderay',
+                    'solr-root-path'    : solrRootPath,
+            ],
+            'html'  : [
+                    'imagesdir': buildContentDir.toString()
+            ]
+    ]
+}
+
+// Tasks modeled after ant file until we have a working build.
+
+task setupProps {
+    doFirst {
+        // These properties have to be resolved after configuration phase (palantir's constraint)
+        // so we can't use them as input for caches.
+        [
+                ["ivyversions./commons-codec/commons-codec", "commons-codec", "commons-codec"],
+                ["ivyversions.io.dropwizard.metrics.version", "io.dropwizard.metrics", "metrics-core"],
+                ["ivyversions.org.apache.logging.log4j.version", "org.apache.logging.log4j", "log4j-core"],
+                ["ivyversions./org.apache.opennlp/opennlp-tools", "org.apache.opennlp", "opennlp-tools"],
+                ["ivyversions.org.apache.tika.version", "org.apache.tika", "tika-core"],
+                ["ivyversions.org.apache.velocity.tools.version", "org.apache.velocity.tools", "velocity-tools-generic"],
+                ["ivyversions./org.apache.zookeeper/zookeeper", "org.apache.zookeeper", "zookeeper"],
+
+                ["ivy-zookeeper-version", "org.apache.zookeeper", "zookeeper"],
+                ["ivy-log4j-version", "org.apache.logging.log4j", "log4j-core"],
+                ["ivy-tika-version", "org.apache.tika", "tika-core"],
+                ["ivy-opennlp-version", "org.apache.opennlp", "opennlp-tools"],
+                ["ivy-commons-codec-version", "commons-codec", "commons-codec"],
+                ["ivy-velocity-tools-version", "org.apache.velocity.tools", "velocity-tools-generic"],
+                ["ivy-dropwizard-version", "io.dropwizard.metrics", "metrics-core"]
+        ].each { antProp, depGroup, depId ->
+            props[antProp] = getVersion(depGroup, depId, configurations.depVer)
+        }
+
+        // Emit info about properties for clarity.
+        logger.warn("Building ref guide with:\n" + props.collect({ k, v -> "  ${k} -> ${v}" }).join('\n'))
+    }
+}
+
+task buildInit(type: Sync) {
+    dependsOn setupProps
+
+    def dummyAntProject = new org.apache.tools.ant.Project()
+
+    // If replaceable properties change, we have to rerun.
+    inputs.properties props
+
+    from(file("src"), {
+        exclude '**/*.template'
+    })
+
+    from(file("src"), {
+        include '**/*.template'
+        rename '(.+)\\.template', '$1'
+        filteringCharset = 'UTF-8'
+        filter(org.apache.tools.ant.filters.ExpandProperties, project: dummyAntProject)
+    })
+
+    doFirst {
+        props.each { k, v ->
+          dummyAntProject.setProperty(k, v)
+        }
+    }
+
+    into buildContentDir
+}
+
+task buildNavDataFiles(type: JavaExec) {
+    dependsOn buildInit, classes
+    classpath = sourceSets.main.runtimeClasspath
+
+    main = 'BuildNavDataFiles'
+    workingDir = buildContentDir
+
+    args([
+            "${buildContentDir}",
+            "${mainPage}"
+    ])
+
+    doFirst {
+        // Remove previously generated files first.
+        [
+                "scrollnav.json",
+                "sidebar.json"
+        ].each { name ->
+            project.delete(file("${buildContentDir}/_data/${name}"))
+        }
+    }
+}
+
+task bareBonesAsciiDoctor {
+    dependsOn buildNavDataFiles
+
+    doLast {
+        // Regenerate fully.
+        project.delete(bareBonesDir)
+        bareBonesDir.mkdirs()
+
+        // Convert each file separately so that folders are preserved.
+        Path source = buildContentDir.toPath().toAbsolutePath().normalize()
+        Path target = bareBonesDir.toPath().toAbsolutePath().normalize()
+
+        Asciidoctor adoc = Asciidoctor.Factory.create()
+        fileTree(source, {
+            include "**/*.adoc"
+            exclude "**/_*"
+        }).each { file ->
+            Path relative = source.relativize(file.toPath())
+            Path targetDir = target.resolve(relative).getParent()
+
+            def opts = OptionsBuilder.options()
+                    .backend('html5')
+                    .docType("book")
+                    .headerFooter(false)
+                    .safe(SafeMode.UNSAFE)
+                    .baseDir(source.toFile())
+                    .toDir(targetDir.toFile())
+                    .destinationDir(targetDir.toFile())
+                    .mkDirs(true)
+                    .attributes(asciiDocAttrs.common + asciiDocAttrs.html + props)
+
+            adoc.convertFile(file, opts)
+        }
+    }
+}
+
+task bareBonesHtmlValidation(type: JavaExec) {
+    dependsOn bareBonesAsciiDoctor
+    description("Builds (w/o Jekyll) a very simple html version of the guide and runs link/anchor validation on it")
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'CheckLinksAndAnchors'
+    workingDir = buildContentDir
+
+    args([
+            "${bareBonesDir}",
+            "-bare-bones"
+    ])
+
+    if (project.hasProperty("local.javadocs")) {
+        args += "-check-all-relative-links"
+    }
+}
+
+task buildSiteJekyll(type: com.github.jrubygradle.JRubyExec) {
+    dependsOn buildNavDataFiles
+
+    inputs.dir buildContentDir
+    outputs.dir file("${buildDir}/html-site")
+
+    script 'jekyll'
+    scriptArgs 'build' //, '--verbose'
+    workingDir buildContentDir
+}
+
+task buildSite(type: JavaExec) {
+    group "Documentation"
+    description "Builds an HTML Site w/Jekyll and verifies the anchors+links are valid"
+
+    dependsOn buildSiteJekyll
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'CheckLinksAndAnchors'
+    workingDir = buildContentDir
+
+    args([
+            file("${buildDir}/html-site"),
+    ])
+
+    if (project.hasProperty("local.javadocs")) {
+        args += "-check-all-relative-links"
+    }
+}
+
+check.dependsOn bareBonesHtmlValidation
diff --git a/solr/solrj/build.gradle b/solr/solrj/build.gradle
new file mode 100644
index 0000000..c77d2dc
--- /dev/null
+++ b/solr/solrj/build.gradle
@@ -0,0 +1,55 @@
+
+apply plugin: 'java-library'
+
+dependencies {
+  api 'org.slf4j:slf4j-api'
+  implementation 'org.slf4j:jcl-over-slf4j'
+
+  api 'commons-io:commons-io'
+  api 'org.apache.commons:commons-math3'
+
+  api 'org.eclipse.jetty.http2:http2-client'
+  api 'org.eclipse.jetty.http2:http2-http-client-transport'
+  api 'org.eclipse.jetty:jetty-util'
+  api 'org.eclipse.jetty:jetty-http'
+  api 'org.eclipse.jetty:jetty-alpn-java-client'
+  api 'org.eclipse.jetty:jetty-alpn-client'
+
+  api('org.apache.httpcomponents:httpmime', {
+    exclude group: "commons-codec", module: "commons-codec"
+    exclude group: "commons-logging", module: "commons-logging"
+  })
+
+  api 'io.netty:netty-buffer'
+  api 'io.netty:netty-codec'
+  api 'io.netty:netty-common'
+  api 'io.netty:netty-handler'
+  api 'io.netty:netty-resolver'
+  api 'io.netty:netty-transport'
+  api 'io.netty:netty-transport-native-epoll'
+  api 'io.netty:netty-transport-native-unix-common'
+
+  api('org.apache.zookeeper:zookeeper', {
+    exclude group: "org.apache.yetus", module: "audience-annotations"
+    exclude group: "io.netty", module: "netty-all"
+    exclude group: "log4j", module: "log4j"
+    exclude group: "org.slf4j", module: "slf4j-log4j12"
+  })
+
+  api('org.codehaus.woodstox:woodstox-core-asl', {
+    exclude group: "javax.xml.stream", module: "stax-api"
+  })
+
+  testImplementation project(':solr:test-framework')
+  testImplementation 'org.eclipse.jetty:jetty-webapp'
+  testImplementation 'org.eclipse.jetty:jetty-alpn-java-server'
+  testImplementation 'org.restlet.jee:org.restlet.ext.servlet'
+  testImplementation 'org.objenesis:objenesis'
+  testImplementation('org.mockito:mockito-core', {
+    exclude group: "net.bytebuddy", module: "byte-buddy-agent"
+  })
+  testImplementation("org.apache.logging.log4j:log4j-slf4j-impl", {
+    exclude group: "org.apache.logging.log4j", module: "log4j-api"
+  })
+  testImplementation "org.hsqldb:hsqldb"
+}
diff --git a/solr/test-framework/build.gradle b/solr/test-framework/build.gradle
new file mode 100644
index 0000000..6b6eca9
--- /dev/null
+++ b/solr/test-framework/build.gradle
@@ -0,0 +1,15 @@
+
+apply plugin: 'java-library'
+
+dependencies {
+  api project(':solr:core')
+  api project(':solr:solrj')
+  api project(':lucene:test-framework')
+  api project(':lucene:analysis:common')
+
+  api 'org.apache.logging.log4j:log4j-core'
+  api 'io.opentracing:opentracing-mock'
+
+  implementation 'io.dropwizard.metrics:metrics-jetty9'
+  implementation 'com.lmax:disruptor'
+}
diff --git a/solr/webapp/build.gradle b/solr/webapp/build.gradle
new file mode 100644
index 0000000..f915c93
--- /dev/null
+++ b/solr/webapp/build.gradle
@@ -0,0 +1,37 @@
+plugins {
+  id 'java'
+  id 'war'
+}
+
+configurations {
+  war {}
+}
+
+dependencies {
+  implementation(project(":solr:core"), {
+    exclude module: "server"
+
+    // Exclude additional deps from core and logging deps to sync up with ant.
+    // This is suspicious that we have to do it though.
+    exclude group: "org.apache.kerby", module: "kerby-config"
+    exclude group: "org.apache.kerby", module: "kerby-util"
+    exclude group: "org.apache.kerby", module: "kerb-crypto"
+    exclude group: "org.slf4j"
+    exclude group: "org.apache.logging.log4j"
+  })
+}
+
+war {
+  // Why are they in the source code at all if they're excluded from the distribution?
+  exclude "libs/angular-cookies.js"
+  exclude "libs/angular-route.js"
+  exclude "libs/angular-sanitize.js"
+  exclude "libs/angular-utf8-base.js"
+  exclude "libs/angular.js"
+  exclude "libs/chosen.jquery.js"
+}
+
+// Expose 'war' archive as an artifact so that it can be packaged in the distribution.
+artifacts {
+  war tasks.war
+}
\ No newline at end of file
diff --git a/versions.lock b/versions.lock
new file mode 100644
index 0000000..d02ff02
--- /dev/null
+++ b/versions.lock
@@ -0,0 +1,230 @@
+# Run ./gradlew --write-locks to regenerate this file
+com.adobe.xmp:xmpcore:5.1.3 (1 constraints: 0b050a36)
+com.beust:jcommander:1.35 (1 constraints: b50c1901)
+com.carrotsearch:hppc:0.8.1 (2 constraints: af0fd8a6)
+com.carrotsearch.randomizedtesting:randomizedtesting-runner:2.7.2 (1 constraints: 0d050c36)
+com.carrotsearch.thirdparty:simple-xml-safe:2.7.1 (1 constraints: a60a82ca)
+com.cybozu.labs:langdetect:1.1-20120112 (1 constraints: 5c066d5e)
+com.drewnoakes:metadata-extractor:2.11.0 (1 constraints: 3605323b)
+com.epam:parso:2.0.9 (1 constraints: 0d05fe35)
+com.fasterxml.jackson.core:jackson-annotations:2.9.9 (2 constraints: 0a1d1637)
+com.fasterxml.jackson.core:jackson-core:2.9.9 (3 constraints: 23350c6a)
+com.fasterxml.jackson.core:jackson-databind:2.9.9.3 (3 constraints: 741a6389)
+com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.9.9 (1 constraints: 16051936)
+com.github.ben-manes.caffeine:caffeine:2.8.0 (1 constraints: 0c050d36)
+com.github.jnr:jffi:1.2.18 (1 constraints: b20902ab)
+com.github.jnr:jnr-constants:0.9.12 (4 constraints: ed2c9d5d)
+com.github.jnr:jnr-enxio:0.19 (2 constraints: 2a167d08)
+com.github.jnr:jnr-netdb:1.1.6 (1 constraints: 7e0952a1)
+com.github.jnr:jnr-posix:3.0.49 (2 constraints: f0161b5b)
+com.github.jnr:jnr-unixsocket:0.20 (1 constraints: 4a09d497)
+com.github.virtuald:curvesapi:1.04 (1 constraints: d904f330)
+com.github.zafarkhaja:java-semver:0.9.0 (1 constraints: 0b050636)
+com.google.code.findbugs:jsr305:3.0.2 (1 constraints: 170aecb4)
+com.google.errorprone:error_prone_annotations:2.1.3 (1 constraints: 180aebb4)
+com.google.guava:guava:25.1-jre (1 constraints: 4a06b047)
+com.google.j2objc:j2objc-annotations:1.1 (1 constraints: b609eba0)
+com.google.protobuf:protobuf-java:3.7.1 (1 constraints: 0d051036)
+com.google.re2j:re2j:1.2 (1 constraints: a7041c2c)
+com.googlecode.juniversalchardet:juniversalchardet:1.0.3 (1 constraints: 0605f335)
+com.googlecode.mp4parser:isoparser:1.1.22 (1 constraints: 38052d3b)
+com.headius:backport9:1.1 (1 constraints: 1a098c8e)
+com.headius:invokebinder:1.11 (1 constraints: 4b09d797)
+com.headius:modulator:1.0 (1 constraints: 19098b8e)
+com.headius:options:1.4 (1 constraints: 1d098f8e)
+com.healthmarketscience.jackcess:jackcess:2.1.12 (1 constraints: 3805313b)
+com.healthmarketscience.jackcess:jackcess-encrypt:2.1.4 (1 constraints: 0905fc35)
+com.ibm.icu:icu4j:62.1 (1 constraints: dd040c31)
+com.jayway.jsonpath:json-path:2.4.0 (1 constraints: 08050136)
+com.jcraft:jzlib:1.1.3 (1 constraints: 7b094fa1)
+com.lmax:disruptor:3.4.2 (1 constraints: 0b050836)
+com.martiansoftware:nailgun-server:0.9.1 (1 constraints: 800960a1)
+com.pff:java-libpst:0.8.1 (1 constraints: 0b050436)
+com.rometools:rome:1.5.1 (1 constraints: 09050036)
+com.rometools:rome-utils:1.5.1 (1 constraints: 09050036)
+com.sun.mail:gimap:1.5.1 (1 constraints: 09050036)
+com.sun.mail:javax.mail:1.5.1 (2 constraints: 830d2844)
+com.tdunning:t-digest:3.1 (1 constraints: a804212c)
+com.vaadin.external.google:android-json:0.0.20131108.vaadin1 (1 constraints: 34092a9e)
+commons-cli:commons-cli:1.2 (1 constraints: a7041c2c)
+commons-codec:commons-codec:1.11 (1 constraints: d704f230)
+commons-collections:commons-collections:3.2.2 (1 constraints: 09050236)
+commons-fileupload:commons-fileupload:1.3.3 (1 constraints: 0905fc35)
+commons-io:commons-io:2.5 (2 constraints: be142680)
+commons-logging:commons-logging:1.2 (2 constraints: c8149e7f)
+de.l3s.boilerpipe:boilerpipe:1.1.0 (1 constraints: 0405f335)
+io.dropwizard.metrics:metrics-core:4.0.5 (5 constraints: 204326bf)
+io.dropwizard.metrics:metrics-graphite:4.0.5 (1 constraints: 0b050436)
+io.dropwizard.metrics:metrics-jetty9:4.0.5 (1 constraints: 0b050436)
+io.dropwizard.metrics:metrics-jmx:4.0.5 (1 constraints: 0b050436)
+io.dropwizard.metrics:metrics-jvm:4.0.5 (1 constraints: 0b050436)
+io.jaegertracing:jaeger-core:0.35.5 (1 constraints: 970d1034)
+io.jaegertracing:jaeger-thrift:0.35.5 (1 constraints: 3f053f3b)
+io.netty:netty-buffer:4.1.29.Final (4 constraints: 5b3421e8)
+io.netty:netty-codec:4.1.29.Final (2 constraints: fc135782)
+io.netty:netty-common:4.1.29.Final (5 constraints: 88482df2)
+io.netty:netty-handler:4.1.29.Final (1 constraints: 5a076061)
+io.netty:netty-resolver:4.1.29.Final (2 constraints: 0b15d4b5)
+io.netty:netty-transport:4.1.29.Final (5 constraints: 7847682a)
+io.netty:netty-transport-native-epoll:4.1.29.Final (1 constraints: 5a076061)
+io.netty:netty-transport-native-unix-common:4.1.29.Final (2 constraints: 081ace05)
+io.opentracing:opentracing-api:0.33.0 (5 constraints: 4c3c8052)
+io.opentracing:opentracing-mock:0.33.0 (1 constraints: 3805343b)
+io.opentracing:opentracing-noop:0.33.0 (3 constraints: 7c2142bd)
+io.opentracing:opentracing-util:0.33.0 (3 constraints: f61f583b)
+io.prometheus:simpleclient:0.2.0 (3 constraints: fe242db8)
+io.prometheus:simpleclient_common:0.2.0 (2 constraints: e8159ecb)
+io.prometheus:simpleclient_httpserver:0.2.0 (1 constraints: 0405f135)
+io.sgr:s2-geometry-library-java:1.0.0 (1 constraints: 0305f035)
+javax.activation:activation:1.1.1 (3 constraints: 1017445c)
+javax.servlet:javax.servlet-api:3.1.0 (3 constraints: 75209943)
+joda-time:joda-time:2.9.9 (1 constraints: 8a0972a1)
+junit:junit:4.12 (2 constraints: 3e1e6104)
+net.arnx:jsonic:1.2.7 (2 constraints: db10d4d1)
+net.hydromatic:eigenbase-properties:1.1.5 (1 constraints: 0905f835)
+net.sourceforge.argparse4j:argparse4j:0.8.1 (1 constraints: 0b050436)
+net.sourceforge.nekohtml:nekohtml:1.9.17 (1 constraints: 4405503b)
+net.thisptr:jackson-jq:0.0.8 (1 constraints: 0a05f335)
+org.antlr:antlr4-runtime:4.5.1-1 (1 constraints: 6a05b240)
+org.apache.calcite:calcite-core:1.18.0 (1 constraints: 3c05413b)
+org.apache.calcite:calcite-linq4j:1.18.0 (1 constraints: 3c05413b)
+org.apache.calcite.avatica:avatica-core:1.13.0 (1 constraints: 3705323b)
+org.apache.commons:commons-collections4:4.2 (1 constraints: aa04252c)
+org.apache.commons:commons-compress:1.18 (1 constraints: de04f930)
+org.apache.commons:commons-configuration2:2.1.1 (1 constraints: 0605f935)
+org.apache.commons:commons-exec:1.3 (1 constraints: a8041d2c)
+org.apache.commons:commons-lang3:3.8.1 (7 constraints: 33673f05)
+org.apache.commons:commons-math3:3.6.1 (1 constraints: 0c050d36)
+org.apache.commons:commons-text:1.6 (1 constraints: ab04202c)
+org.apache.curator:curator-client:2.13.0 (1 constraints: 3805383b)
+org.apache.curator:curator-framework:2.13.0 (1 constraints: 3805383b)
+org.apache.curator:curator-recipes:2.13.0 (1 constraints: 3805383b)
+org.apache.hadoop:hadoop-annotations:3.2.0 (1 constraints: 07050036)
+org.apache.hadoop:hadoop-auth:3.2.0 (1 constraints: 07050036)
+org.apache.hadoop:hadoop-common:3.2.0 (1 constraints: 07050036)
+org.apache.hadoop:hadoop-hdfs-client:3.2.0 (1 constraints: 07050036)
+org.apache.htrace:htrace-core4:4.1.0-incubating (1 constraints: 58090086)
+org.apache.httpcomponents:httpclient:4.5.6 (2 constraints: 6514987d)
+org.apache.httpcomponents:httpcore:4.4.10 (2 constraints: 9015d5cd)
+org.apache.httpcomponents:httpmime:4.5.6 (1 constraints: 11051436)
+org.apache.james:apache-mime4j-core:0.8.2 (1 constraints: 0c050536)
+org.apache.james:apache-mime4j-dom:0.8.2 (1 constraints: 0c050536)
+org.apache.kerby:kerb-core:1.0.1 (3 constraints: f11c583a)
+org.apache.kerby:kerb-crypto:1.0.1 (1 constraints: 860b05e6)
+org.apache.kerby:kerb-util:1.0.1 (1 constraints: 0405f135)
+org.apache.kerby:kerby-asn1:1.0.1 (2 constraints: 001155df)
+org.apache.kerby:kerby-config:1.0.1 (1 constraints: 860b05e6)
+org.apache.kerby:kerby-pkix:1.0.1 (2 constraints: 741065ca)
+org.apache.kerby:kerby-util:1.0.1 (2 constraints: 6518bdb6)
+org.apache.logging.log4j:log4j-api:2.11.2 (3 constraints: c124e473)
+org.apache.logging.log4j:log4j-core:2.11.2 (2 constraints: 09164024)
+org.apache.logging.log4j:log4j-slf4j-impl:2.11.2 (1 constraints: 3805343b)
+org.apache.opennlp:opennlp-tools:1.9.1 (1 constraints: 0d050c36)
+org.apache.pdfbox:fontbox:2.0.12 (1 constraints: 37052d3b)
+org.apache.pdfbox:jempbox:1.8.16 (1 constraints: 42054b3b)
+org.apache.pdfbox:pdfbox:2.0.12 (1 constraints: 37052d3b)
+org.apache.pdfbox:pdfbox-tools:2.0.12 (1 constraints: 37052d3b)
+org.apache.poi:poi:4.0.0 (1 constraints: 0605ff35)
+org.apache.poi:poi-ooxml:4.0.0 (1 constraints: 0605ff35)
+org.apache.poi:poi-ooxml-schemas:4.0.0 (1 constraints: 0605ff35)
+org.apache.poi:poi-scratchpad:4.0.0 (1 constraints: 0605ff35)
+org.apache.thrift:libthrift:0.12.0 (1 constraints: 8d0dfa33)
+org.apache.tika:tika-core:1.19.1 (1 constraints: 3e05453b)
+org.apache.tika:tika-java7:1.19.1 (1 constraints: 3e05453b)
+org.apache.tika:tika-parsers:1.19.1 (1 constraints: 3e05453b)
+org.apache.tika:tika-xmp:1.19.1 (1 constraints: 3e05453b)
+org.apache.velocity:velocity-engine-core:2.0 (3 constraints: 973bcd79)
+org.apache.velocity.tools:velocity-tools-generic:3.0 (1 constraints: 00136415)
+org.apache.velocity.tools:velocity-tools-view:3.0 (1 constraints: 7a14126a)
+org.apache.velocity.tools:velocity-tools-view-jsp:3.0 (1 constraints: a704202c)
+org.apache.xmlbeans:xmlbeans:3.0.1 (1 constraints: 0605fb35)
+org.apache.zookeeper:zookeeper:3.5.5 (1 constraints: 0f050e36)
+org.apache.zookeeper:zookeeper-jute:3.5.5 (1 constraints: 8d0d3928)
+org.asciidoctor:asciidoctorj:1.6.2 (1 constraints: 0b050436)
+org.asciidoctor:asciidoctorj-api:1.6.2 (1 constraints: e30cfb0d)
+org.aspectj:aspectjrt:1.8.0 (1 constraints: 0b050836)
+org.bitbucket.b_c:jose4j:0.6.5 (1 constraints: 0d050236)
+org.bouncycastle:bcmail-jdk15on:1.60 (1 constraints: db04fb30)
+org.bouncycastle:bcpkix-jdk15on:1.60 (1 constraints: db04fb30)
+org.bouncycastle:bcprov-jdk15on:1.60 (1 constraints: db04fb30)
+org.brotli:dec:0.1.2 (1 constraints: 0505f035)
+org.carrot2:carrot2-mini:3.16.2 (1 constraints: 3e05493b)
+org.carrot2:morfologik-fsa:2.1.5 (1 constraints: d70d9836)
+org.carrot2:morfologik-polish:2.1.5 (1 constraints: 0a05fd35)
+org.carrot2:morfologik-stemming:2.1.5 (2 constraints: 0b12640c)
+org.carrot2.attributes:attributes-binder:1.3.3 (1 constraints: a30a73ca)
+org.carrot2.shaded:carrot2-guava:18.0 (2 constraints: b31b3b7b)
+org.ccil.cowan.tagsoup:tagsoup:1.2.1 (1 constraints: 0605f735)
+org.checkerframework:checker-qual:2.0.0 (1 constraints: 140ae5b4)
+org.codehaus.janino:commons-compiler:3.0.9 (2 constraints: d910f7d1)
+org.codehaus.janino:janino:3.0.9 (1 constraints: 0e050336)
+org.codehaus.mojo:animal-sniffer-annotations:1.14 (1 constraints: ea09d5aa)
+org.codehaus.woodstox:stax2-api:3.1.4 (2 constraints: 241635f1)
+org.codehaus.woodstox:woodstox-core-asl:4.4.1 (1 constraints: 0b050c36)
+org.eclipse.jetty:jetty-alpn-client:9.4.19.v20190610 (3 constraints: d12c8400)
+org.eclipse.jetty:jetty-alpn-java-client:9.4.19.v20190610 (1 constraints: 8007517d)
+org.eclipse.jetty:jetty-alpn-java-server:9.4.19.v20190610 (1 constraints: 8007517d)
+org.eclipse.jetty:jetty-alpn-server:9.4.19.v20190610 (2 constraints: 231beedd)
+org.eclipse.jetty:jetty-client:9.4.19.v20190610 (1 constraints: ce1741ae)
+org.eclipse.jetty:jetty-continuation:9.4.19.v20190610 (2 constraints: 5d186efb)
+org.eclipse.jetty:jetty-deploy:9.4.19.v20190610 (1 constraints: 8007517d)
+org.eclipse.jetty:jetty-http:9.4.19.v20190610 (5 constraints: 8b497e4f)
+org.eclipse.jetty:jetty-io:9.4.19.v20190610 (8 constraints: 0f7eb132)
+org.eclipse.jetty:jetty-jmx:9.4.19.v20190610 (1 constraints: 8007517d)
+org.eclipse.jetty:jetty-rewrite:9.4.19.v20190610 (1 constraints: 8007517d)
+org.eclipse.jetty:jetty-security:9.4.19.v20190610 (2 constraints: ea172ade)
+org.eclipse.jetty:jetty-server:9.4.19.v20190610 (6 constraints: bd5e38f5)
+org.eclipse.jetty:jetty-servlet:9.4.19.v20190610 (2 constraints: 641789bf)
+org.eclipse.jetty:jetty-servlets:9.4.19.v20190610 (1 constraints: 8007517d)
+org.eclipse.jetty:jetty-util:9.4.19.v20190610 (7 constraints: 77648009)
+org.eclipse.jetty:jetty-webapp:9.4.19.v20190610 (2 constraints: 72178fc0)
+org.eclipse.jetty:jetty-xml:9.4.19.v20190610 (3 constraints: 56274720)
+org.eclipse.jetty.alpn:alpn-api:1.1.3.v20160715 (1 constraints: 6513848a)
+org.eclipse.jetty.http2:http2-client:9.4.19.v20190610 (2 constraints: 4d1ff83f)
+org.eclipse.jetty.http2:http2-common:9.4.19.v20190610 (3 constraints: 242bc31f)
+org.eclipse.jetty.http2:http2-hpack:9.4.19.v20190610 (2 constraints: 50198f5c)
+org.eclipse.jetty.http2:http2-http-client-transport:9.4.19.v20190610 (1 constraints: 8007517d)
+org.eclipse.jetty.http2:http2-server:9.4.19.v20190610 (1 constraints: 8007517d)
+org.gagravarr:vorbis-java-core:0.8 (1 constraints: ac041f2c)
+org.gagravarr:vorbis-java-tika:0.8 (1 constraints: ac041f2c)
+org.hamcrest:hamcrest-core:1.3 (2 constraints: 730ad9bf)
+org.jdom:jdom2:2.0.6 (1 constraints: 0a05fb35)
+org.jruby:dirgra:0.3 (1 constraints: 1b098b8e)
+org.jruby:jruby:9.2.6.0 (1 constraints: 490d7d28)
+org.jruby:jruby-core:9.2.6.0 (1 constraints: 0f08b57d)
+org.jruby:jruby-stdlib:9.2.6.0 (1 constraints: 0f08b57d)
+org.jruby.jcodings:jcodings:1.0.41 (2 constraints: e3124361)
+org.jruby.joni:joni:2.1.25 (1 constraints: b00903ab)
+org.jsoup:jsoup:1.11.3 (1 constraints: 38052f3b)
+org.locationtech.spatial4j:spatial4j:0.7 (1 constraints: ab041e2c)
+org.ow2.asm:asm:5.1 (1 constraints: aa04272c)
+org.ow2.asm:asm-commons:5.1 (1 constraints: aa04272c)
+org.restlet.jee:org.restlet:2.3.0 (2 constraints: e3159ee6)
+org.restlet.jee:org.restlet.ext.servlet:2.3.0 (1 constraints: 0705fe35)
+org.rrd4j:rrd4j:3.5 (1 constraints: ac04252c)
+org.slf4j:jcl-over-slf4j:1.7.25 (1 constraints: 4005473b)
+org.slf4j:slf4j-api:1.7.25 (19 constraints: aaff8c21)
+org.slf4j:slf4j-simple:1.7.25 (1 constraints: 4005473b)
+org.tallison:jmatio:1.5 (1 constraints: aa041f2c)
+org.tukaani:xz:1.8 (1 constraints: ad04222c)
+ua.net.nlp:morfologik-ukrainian-search:3.9.0 (1 constraints: 0e051536)
+xerces:xercesImpl:2.9.1 (2 constraints: f613d869)
+
+[Test dependencies]
+com.sun.jersey:jersey-servlet:1.19.4 (1 constraints: 4105483b)
+net.bytebuddy:byte-buddy:1.9.3 (2 constraints: 2510faaf)
+org.apache.derby:derby:10.9.1.0 (1 constraints: 9b054946)
+org.apache.hadoop:hadoop-hdfs:3.2.0 (1 constraints: 07050036)
+org.apache.hadoop:hadoop-minikdc:3.2.0 (1 constraints: 07050036)
+org.apache.kerby:kerb-admin:1.0.1 (1 constraints: 0405f135)
+org.apache.kerby:kerb-client:1.0.1 (1 constraints: 0405f135)
+org.apache.kerby:kerb-common:1.0.1 (1 constraints: 0405f135)
+org.apache.kerby:kerb-identity:1.0.1 (1 constraints: 0405f135)
+org.apache.kerby:kerb-server:1.0.1 (1 constraints: 0405f135)
+org.apache.kerby:kerb-simplekdc:1.0.1 (1 constraints: 0405f135)
+org.apache.kerby:kerby-kdc:1.0.1 (1 constraints: 0405f135)
+org.hsqldb:hsqldb:2.4.0 (1 constraints: 08050136)
+org.locationtech.jts:jts-core:1.15.0 (1 constraints: 3905383b)
+org.mockito:mockito-core:2.23.4 (1 constraints: 3d05403b)
+org.objenesis:objenesis:2.6 (2 constraints: 5f0ffb79)
+org.slf4j:log4j-over-slf4j:1.7.25 (1 constraints: 4005473b)
diff --git a/versions.props b/versions.props
new file mode 100644
index 0000000..bbca163
--- /dev/null
+++ b/versions.props
@@ -0,0 +1,111 @@
+com.adobe.xmp:xmpcore=5.1.3
+com.carrotsearch.randomizedtesting:*=2.7.2
+com.carrotsearch:hppc=0.8.1
+com.cybozu.labs:langdetect=1.1-20120112
+com.drewnoakes:metadata-extractor=2.11.0
+com.epam:parso=2.0.9
+com.fasterxml.jackson*:*=2.9.9
+com.fasterxml.woodstox:*=4.4.1
+com.github.ben-manes.caffeine:caffeine=2.8.0
+com.github.virtuald:curvesapi=1.04
+com.github.zafarkhaja:java-semver=0.9.0
+com.google.guava:guava=25.1-jre
+com.google.protobuf:protobuf-java=3.7.1
+com.google.re2j:re2j=1.2
+com.googlecode.juniversalchardet:juniversalchardet=1.0.3
+com.googlecode.mp4parser:isoparser=1.1.22
+com.healthmarketscience.jackcess:jackcess-encrypt=2.1.4
+com.healthmarketscience.jackcess:jackcess=2.1.12
+com.ibm.icu:icu4j=62.1
+com.jayway.jsonpath:json-path=2.4.0
+com.lmax:disruptor=3.4.2
+com.pff:java-libpst=0.8.1
+com.rometools:*=1.5.1
+com.sun.jersey:*=1.19
+com.sun.jersey:jersey-json=1.19.4
+com.sun.mail:*=1.5.1
+com.tdunning:t-digest=3.1
+com.vaadin.external.google:android-json=0.0.20131108.vaadin1
+commons-beanutils:commons-beanutils=1.9.3
+commons-cli:commons-cli=1.2
+commons-codec:commons-codec=1.11
+commons-collections:commons-collections=3.2.2
+commons-fileupload:commons-fileupload=1.3.3
+commons-io:commons-io=2.5
+commons-logging:commons-logging=1.1.3
+de.l3s.boilerpipe:boilerpipe=1.1.0
+info.ganglia.gmetric4j:gmetric4j=1.0.7
+io.dropwizard.metrics:*=4.0.5
+io.netty:*=4.1.29.Final
+io.opentracing:*=0.33.0
+io.prometheus:*=0.2.0
+io.sgr:s2-geometry-library-java=1.0.0
+javax.activation:activation=1.1.1
+javax.servlet:javax.servlet-api=3.1.0
+junit:junit=4.12
+net.arnx:jsonic=1.2.7
+net.bytebuddy:byte-buddy=1.9.3
+net.hydromatic:eigenbase-properties=1.1.5
+net.sourceforge.argparse4j:argparse4j=0.8.1
+net.sourceforge.nekohtml:nekohtml=1.9.17
+net.thisptr:jackson-jq=0.0.8
+org.antlr:antlr4-runtime=4.5.1-1
+org.apache.calcite.avatica:avatica-core=1.13.0
+org.apache.calcite:*=1.18.0
+org.apache.commons:commons-collections4:4.2
+org.apache.commons:commons-compress=1.18
+org.apache.commons:commons-configuration2=2.1.1
+org.apache.commons:commons-exec=1.3
+org.apache.commons:commons-lang3=3.6
+org.apache.commons:commons-math3=3.6.1
+org.apache.commons:commons-text=1.6
+org.apache.curator:*=2.13.0
+org.apache.derby:derby=10.9.1.0
+org.apache.hadoop:*=3.2.0
+org.apache.htrace:htrace-core4=4.1.0-incubating
+org.apache.httpcomponents:httpclient=4.5.6
+org.apache.httpcomponents:httpcore=4.4.10
+org.apache.httpcomponents:httpmime=4.5.6
+org.apache.james:*=0.8.2
+org.apache.kerby:*=1.0.1
+org.apache.logging.log4j:*=2.11.2
+org.apache.opennlp:opennlp-tools=1.9.1
+org.apache.pdfbox:*=2.0.12
+org.apache.pdfbox:jbig2-imageio=3.0.2
+org.apache.pdfbox:jempbox=1.8.16
+org.apache.poi:*=4.0.0
+org.apache.tika:*=1.19.1
+org.apache.velocity.tools:*=3.0
+org.apache.xmlbeans:xmlbeans=3.0.1
+org.apache.zookeeper:*=3.5.5
+org.asciidoctor:asciidoctorj=1.6.2
+org.aspectj:aspectjrt=1.8.0
+org.bitbucket.b_c:jose4j=0.6.5
+org.bouncycastle:*=1.60
+org.brotli:dec=0.1.2
+org.carrot2:carrot2-mini=3.16.2
+org.carrot2:morfologik-*=2.1.5
+org.ccil.cowan.tagsoup:tagsoup=1.2.1
+org.codehaus.janino:*=3.0.9
+org.codehaus.woodstox:stax2-api=3.1.4
+org.codehaus.woodstox:woodstox-core-asl=4.4.1
+org.eclipse.jetty.http2:*=9.4.19.v20190610
+org.eclipse.jetty:*=9.4.19.v20190610
+org.gagravarr:*=0.8
+org.hamcrest:*=1.3
+org.hsqldb:hsqldb=2.4.0
+org.jdom:jdom2=2.0.6
+org.jsoup:jsoup=1.11.3
+org.locationtech.jts:jts-core=1.15.0
+org.locationtech.spatial4j:*=0.7
+org.mockito:mockito-core=2.23.4
+org.objenesis:objenesis=2.6
+org.ow2.asm:*=5.1
+org.restlet.jee:*=2.3.0
+org.rrd4j:rrd4j=3.5
+org.slf4j:*=1.7.24
+org.tallison:jmatio=1.5
+org.tukaani:xz=1.8
+ua.net.nlp:morfologik-ukrainian-search=3.9.0
+xerces:xercesImpl=2.9.1
+io.jaegertracing:*=0.35.5
\ No newline at end of file