diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml
index d529df1..b416cb8 100644
--- a/.bazelci/presubmit.yml
+++ b/.bazelci/presubmit.yml
@@ -1,19 +1,41 @@
 ---
+build_targets: &build_targets
+  - "//..."
+  # TODO: Look into broken targets in //toolchains
+  - "-//toolchains/..."
+
+build_targets_bzlmod: &build_targets_bzlmod
+  - "//..."
+  - "-//toolchains/..."
+  # TODO(pcloudy): pkg_tar doesn't work with Bzlmod due to https://github.com/bazelbuild/bazel/issues/14259
+  # Enable once the issue is fixed.
+  - "-//distro/..."
+
+buildifier:
+  version: latest
+  warnings: "all"
 tasks:
-  ubuntu1604:
-    build_targets:
-    - "..."
-  ubuntu1804:
-    build_targets:
-    - "..."
-  ubuntu1804_nojava:
-    build_flags:
-    - "--javabase=@openjdk11_linux_archive//:runtime"
-    build_targets:
-    - "..."
+  ubuntu2004:
+    build_targets: *build_targets
   macos:
-    build_targets:
-    - "..."
+    build_targets: *build_targets
   windows:
-    build_targets:
-    - "..."
+    build_targets: *build_targets
+  ubuntu2004_bzlmod:
+    bazel: last_green
+    platform: ubuntu2004
+    build_flags:
+      - "--config=bzlmod"
+    build_targets: *build_targets_bzlmod
+  macos_bzlmod:
+    bazel: last_green
+    platform: macos
+    build_flags:
+      - "--config=bzlmod"
+    build_targets: *build_targets_bzlmod
+  windows_bzlmod:
+    bazel: last_green
+    platform: windows
+    build_flags:
+      - "--config=bzlmod"
+    build_targets: *build_targets_bzlmod
diff --git a/.bazelrc b/.bazelrc
new file mode 100644
index 0000000..e04bf92
--- /dev/null
+++ b/.bazelrc
@@ -0,0 +1,6 @@
+build:bzlmod --experimental_enable_bzlmod
+# @local_config_cc and @local_config_xcode are no longer available as canonical repo name,
+# therefore we have to override the default value of the following flags.
+# TODO: Remove after fixing https://github.com/bazelbuild/bazel/issues/14279
+build:bzlmod --crosstool_top=@rules_cc.0.0.1.cc_configure.local_config_cc//:toolchain
+build:bzlmod --xcode_version_config=@rules_cc.0.0.1.cc_configure.local_config_xcode//:host_xcodes
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..8f95963
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,9 @@
+# This the official list of Bazel authors for copyright purposes.
+# This file is distinct from the CONTRIBUTORS files.
+# See the latter for an explanation.
+
+# Names should be added to this file as:
+# Name or Organization <email address>
+# The email address is not required for organizations.
+
+Google Inc.
diff --git a/BUILD b/BUILD
index 1f6a0cf..b87a792 100644
--- a/BUILD
+++ b/BUILD
@@ -4,10 +4,11 @@ exports_files(["LICENSE"])
 
 filegroup(
     name = "distribution",
-    srcs = glob([
+    srcs = [
         "BUILD",
         "LICENSE",
-        "*.bzl",
-    ]),
+        "//java:srcs",
+        "//toolchains:srcs",
+    ],
     visibility = ["@//distro:__pkg__"],
 )
diff --git a/CODEOWNERS b/CODEOWNERS
index 132299b..a54ea9b 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -14,4 +14,4 @@
 # Component owners
 # ----------------
 
-* @iirina @hlopko
\ No newline at end of file
+* @lberki @comius @c-mita @oquenchil @cushon @katre @bazelbuild/java-team
diff --git a/MODULE.bazel b/MODULE.bazel
new file mode 100644
index 0000000..fc61c65
--- /dev/null
+++ b/MODULE.bazel
@@ -0,0 +1,63 @@
+JDK_VERSIONS = [
+    "11",
+    "15",
+    "16",
+    "17",
+]
+
+PLATFORMS = [
+    "linux",
+    "macos",
+    "macos_aarch64",
+    "win",
+]
+
+# Remote JDK repos for those Linux platforms are only defined for JDK 11.
+EXTRA_REMOTE_JDK11_REPOS = [
+    "remotejdk11_linux_aarch64",
+    "remotejdk11_linux_ppc64le",
+    "remotejdk11_linux_s390x",
+]
+
+REMOTE_JDK_REPOS = [("remotejdk" + version + "_" + platform) for version in JDK_VERSIONS for platform in PLATFORMS] + EXTRA_REMOTE_JDK11_REPOS
+
+REMOTE_JAVA_TOOLCHAINS = [("@" + name + "_toolchain_config_repo//:toolchain") for name in REMOTE_JDK_REPOS]
+
+module(
+    name = "rules_java",
+    compatibility_level = 1,
+    toolchains_to_register = [
+        "//toolchains:all",
+        "@local_jdk//:runtime_toolchain_definition",
+    ] + REMOTE_JAVA_TOOLCHAINS,
+    version = "4.0.0",
+)
+
+bazel_dep(name = "platforms", version = "0.0.4")
+bazel_dep(name = "rules_cc", version = "0.0.1")
+
+# rules_proto is required by @remote_java_tools, which is loaded via module extension.
+bazel_dep(name = "rules_proto", version = "4.0.0")
+
+toolchains = use_extension("//java:extensions.bzl", "toolchains")
+
+# Declare remote java tools repos
+use_repo(toolchains, "remote_java_tools")
+
+use_repo(toolchains, "remote_java_tools_linux")
+
+use_repo(toolchains, "remote_java_tools_windows")
+
+use_repo(toolchains, "remote_java_tools_darwin")
+
+# Declare local jdk repo
+use_repo(toolchains, "local_jdk")
+
+# Declare all remote jdk toolchain config repos
+[use_repo(
+    toolchains,
+    repo + "_toolchain_config_repo",
+) for repo in REMOTE_JDK_REPOS]
+
+# Dev dependencies
+bazel_dep(name = "rules_pkg", dev_dependency = True, version = "0.5.1")
diff --git a/README.md b/README.md
index f27134f..8c946de 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,6 @@
 # rules_java
+
+* Postsubmit [![Build status](https://badge.buildkite.com/d4f950ef5f481b8ca066624ba06c238fa1446d84a057ddbf89.svg?branch=master)](https://buildkite.com/bazel/rules-java-java)
+* Postsubmit + Current Bazel Incompatible Flags [![Build status](https://badge.buildkite.com/ef265d270238c02aff65106a0b861abb9265efacdf4af399c3.svg?branch=master)](https://buildkite.com/bazel/rules-java-plus-bazelisk-migrate)
+
 Java Rules for Bazel https://bazel.build.
diff --git a/WORKSPACE b/WORKSPACE
index 2dfeff0..e4bdea6 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,25 +1,30 @@
 workspace(name = "rules_java")
 
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
 http_archive(
     name = "bazel_federation",
-    url = "https://github.com/bazelbuild/bazel-federation/archive/f0e5eda7f0cbfe67f126ef4dacb18c89039b0506.zip", # 2019-09-30
     sha256 = "33222ab7bcc430f1ff1db8788c2e0118b749319dd572476c4fd02322d7d15792",
     strip_prefix = "bazel-federation-f0e5eda7f0cbfe67f126ef4dacb18c89039b0506",
     type = "zip",
+    url = "https://github.com/bazelbuild/bazel-federation/archive/f0e5eda7f0cbfe67f126ef4dacb18c89039b0506.zip",  # 2019-09-30
 )
 
 load("@bazel_federation//:repositories.bzl", "rules_java_deps")
+
 rules_java_deps()
 
 load("@bazel_federation//setup:rules_java.bzl", "rules_java_setup")
+
 rules_java_setup()
 
 #
 # Dependencies for development of rules_java itself.
 #
 load("//:internal_deps.bzl", "rules_java_internal_deps")
+
 rules_java_internal_deps()
 
 load("//:internal_setup.bzl", "rules_java_internal_setup")
+
 rules_java_internal_setup()
diff --git a/WORKSPACE.bzlmod b/WORKSPACE.bzlmod
new file mode 100644
index 0000000..b021fa6
--- /dev/null
+++ b/WORKSPACE.bzlmod
@@ -0,0 +1,2 @@
+# A completely empty WORKSPACE file to replace the original WORKSPACE content when enabling Bzlmod.
+# No WORKSPACE prefix or suffix are added for this file.
diff --git a/distro/BUILD b/distro/BUILD.bazel
similarity index 91%
rename from distro/BUILD
rename to distro/BUILD.bazel
index da2c9b9..71352e8 100644
--- a/distro/BUILD
+++ b/distro/BUILD.bazel
@@ -1,18 +1,15 @@
-package(
-    default_visibility = ["//visibility:private"],
-)
-
 load("@rules_java//java:defs.bzl", "version")
 load("@rules_pkg//:pkg.bzl", "pkg_tar")
 load("@rules_pkg//releasing:defs.bzl", "print_rel_notes")
 
+package(
+    default_visibility = ["//visibility:private"],
+)
+
 # Build the artifact to put on the github release page.
 pkg_tar(
     name = "rules_java-%s" % version,
-    srcs = [
-        "//:distribution",
-        "//java:distribution",
-    ],
+    srcs = ["//:distribution"],
     extension = "tar.gz",
     # It is all source code, so make it read-only.
     mode = "0444",
diff --git a/examples/hello_world/BUILD b/examples/hello_world/BUILD
new file mode 100644
index 0000000..96b2a5f
--- /dev/null
+++ b/examples/hello_world/BUILD
@@ -0,0 +1,7 @@
+load("//java:defs.bzl", "java_binary")
+
+java_binary(
+    name = "hello_world",
+    srcs = ["HelloWorld.java"],
+    main_class = "HelloWorld",
+)
diff --git a/examples/hello_world/HelloWorld.java b/examples/hello_world/HelloWorld.java
new file mode 100644
index 0000000..20331e4
--- /dev/null
+++ b/examples/hello_world/HelloWorld.java
@@ -0,0 +1,19 @@
+// Copyright 2021 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+public class HelloWorld {
+  public static void main (String[] args) {
+    System.out.println("Hello World!\n");
+  }
+}
diff --git a/java/BUILD b/java/BUILD
index c799b9b..67d2c89 100644
--- a/java/BUILD
+++ b/java/BUILD
@@ -1,14 +1,9 @@
 package(default_visibility = ["//visibility:public"])
 
-# TODO(aiuto): Find a way to strip this rule from the distribution tarball.
+licenses(["notice"])
+
 filegroup(
-    name = "distribution",
-    srcs = glob([
-        "**",
-    ]) + [
-        "//java/constraints:srcs",
-    ],
-    visibility = [
-        "@//distro:__pkg__",
-    ],
+    name = "srcs",
+    srcs = glob(["**"]),
+    visibility = ["@//:__pkg__"],
 )
diff --git a/java/constraints/BUILD b/java/constraints/BUILD
deleted file mode 100644
index fbc5407..0000000
--- a/java/constraints/BUILD
+++ /dev/null
@@ -1,70 +0,0 @@
-# Standard constraint_settings and constraint_values for Java.
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-filegroup(
-    name = "srcs",
-    srcs = ["BUILD"],
-)
-
-constraint_setting(name = "runtime")
-
-constraint_value(
-    name = "jdk8",
-    constraint_setting = ":runtime",
-)
-
-constraint_value(
-    name = "jdk9",
-    constraint_setting = ":runtime",
-)
-
-constraint_value(
-    name = "jdk10",
-    constraint_setting = ":runtime",
-)
-
-constraint_value(
-    name = "jdk11",
-    constraint_setting = ":runtime",
-)
-
-constraint_value(
-    name = "jdk12",
-    constraint_setting = ":runtime",
-)
-
-constraint_setting(name = "language")
-
-# visibility
-constraint_value(
-    name = "java7",
-    constraint_setting = ":language",
-)
-
-constraint_value(
-    name = "java8",
-    constraint_setting = ":language",
-)
-
-constraint_value(
-    name = "java9",
-    constraint_setting = ":language",
-)
-
-constraint_value(
-    name = "java10",
-    constraint_setting = ":language",
-)
-
-constraint_value(
-    name = "java11",
-    constraint_setting = ":language",
-)
-
-constraint_value(
-    name = "java12",
-    constraint_setting = ":language",
-)
diff --git a/java/defs.bzl b/java/defs.bzl
index 6d24f52..4c179a5 100644
--- a/java/defs.bzl
+++ b/java/defs.bzl
@@ -15,7 +15,7 @@
 
 _MIGRATION_TAG = "__JAVA_RULES_MIGRATION_DO_NOT_USE_WILL_BREAK__"
 
-version = "0.1.1"
+version = "4.1.0"
 
 def _add_tags(attrs):
     if "tags" in attrs and attrs["tags"] != None:
@@ -32,6 +32,8 @@ def java_binary(**attrs):
     Args:
       **attrs: Rule attributes
     """
+
+    # buildifier: disable=native-java
     native.java_binary(**_add_tags(attrs))
 
 def java_import(**attrs):
@@ -42,6 +44,8 @@ def java_import(**attrs):
     Args:
       **attrs: Rule attributes
     """
+
+    # buildifier: disable=native-java
     native.java_import(**_add_tags(attrs))
 
 def java_library(**attrs):
@@ -52,6 +56,8 @@ def java_library(**attrs):
     Args:
       **attrs: Rule attributes
     """
+
+    # buildifier: disable=native-java
     native.java_library(**_add_tags(attrs))
 
 def java_lite_proto_library(**attrs):
@@ -62,6 +68,8 @@ def java_lite_proto_library(**attrs):
     Args:
       **attrs: Rule attributes
     """
+
+    # buildifier: disable=native-java
     native.java_lite_proto_library(**_add_tags(attrs))
 
 def java_proto_library(**attrs):
@@ -72,6 +80,8 @@ def java_proto_library(**attrs):
     Args:
       **attrs: Rule attributes
     """
+
+    # buildifier: disable=native-java
     native.java_proto_library(**_add_tags(attrs))
 
 def java_test(**attrs):
@@ -82,6 +92,8 @@ def java_test(**attrs):
     Args:
       **attrs: Rule attributes
     """
+
+    # buildifier: disable=native-java
     native.java_test(**_add_tags(attrs))
 
 def java_package_configuration(**attrs):
@@ -92,6 +104,8 @@ def java_package_configuration(**attrs):
     Args:
       **attrs: Rule attributes
     """
+
+    # buildifier: disable=native-java
     native.java_package_configuration(**_add_tags(attrs))
 
 def java_plugin(**attrs):
@@ -102,6 +116,8 @@ def java_plugin(**attrs):
     Args:
       **attrs: Rule attributes
     """
+
+    # buildifier: disable=native-java
     native.java_plugin(**_add_tags(attrs))
 
 def java_runtime(**attrs):
@@ -112,6 +128,8 @@ def java_runtime(**attrs):
     Args:
       **attrs: Rule attributes
     """
+
+    # buildifier: disable=native-java
     native.java_runtime(**_add_tags(attrs))
 
 def java_toolchain(**attrs):
@@ -122,4 +140,6 @@ def java_toolchain(**attrs):
     Args:
       **attrs: Rule attributes
     """
+
+    # buildifier: disable=native-java
     native.java_toolchain(**_add_tags(attrs))
diff --git a/java/extensions.bzl b/java/extensions.bzl
new file mode 100644
index 0000000..ae51218
--- /dev/null
+++ b/java/extensions.bzl
@@ -0,0 +1,26 @@
+# Copyright 2021 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Module extensions for rules_java."""
+
+load("//java:repositories.bzl", "java_tools_repos", "local_jdk_repo", "remote_jdk11_repos", "remote_jdk15_repos", "remote_jdk16_repos", "remote_jdk17_repos")
+
+def _toolchains_impl(_ctx):
+    java_tools_repos()
+    local_jdk_repo()
+    remote_jdk11_repos()
+    remote_jdk15_repos()
+    remote_jdk16_repos()
+    remote_jdk17_repos()
+
+toolchains = module_extension(implementation = _toolchains_impl)
diff --git a/java/repositories.bzl b/java/repositories.bzl
index 93a4b92..eba6a7a 100644
--- a/java/repositories.bzl
+++ b/java/repositories.bzl
@@ -21,288 +21,442 @@
 # Ideally we'd remove anything in this file except for rules_java_toolchains(),
 # which is being invoked as part of the federation setup.
 
+"""Development and production dependencies of rules_java."""
+
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
 load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
+load("//toolchains:local_java_repository.bzl", "local_java_repository")
+load("//toolchains:remote_java_repository.bzl", "remote_java_repository")
 
-def java_tools_javac9_repos():
+def java_tools_repos():
     maybe(
         http_archive,
-        name = "remote_java_tools_javac9_linux",
-        sha256 = "0bf678d9815c7212564ecc99b3bd3643450c17657becb12a7bbedcf97ece740d",
+        name = "remote_java_tools",
+        sha256 = "b763ee80e5754e593fd6d5be6d7343f905bc8b73d661d36d842b024ca11b6793",
         urls = [
-            "https://mirror.bazel.build/bazel_java_tools/releases/javac9/v3.0/java_tools_javac9_linux-v3.0.zip",
+            "https://mirror.bazel.build/bazel_java_tools/releases/java/v11.5/java_tools-v11.5.zip",
+            "https://github.com/bazelbuild/java_tools/releases/download/java_v11.5/java_tools-v11.5.zip",
         ],
     )
+
     maybe(
         http_archive,
-        name = "remote_java_tools_javac9_windows",
-        sha256 = "9b7e8de98ed2d64ea20a7512f986028ca6375b0fce7637f8d05d1517e7890867",
+        name = "remote_java_tools_linux",
+        sha256 = "ae1eca4546eac6487c6e565f9b409536609c273207220c51e5c94f2a058a5a56",
         urls = [
-            "https://mirror.bazel.build/bazel_java_tools/releases/javac9/v3.0/java_tools_javac9_windows-v3.0.zip",
+            "https://mirror.bazel.build/bazel_java_tools/releases/java/v11.5/java_tools_linux-v11.5.zip",
+            "https://github.com/bazelbuild/java_tools/releases/download/java_v11.5/java_tools_linux-v11.5.zip",
         ],
     )
+
     maybe(
         http_archive,
-        name = "remote_java_tools_javac9_macos",
-        sha256 = "13a94ddf0c421332f0d3be1adbfc833e24a3a3715bab8f1152660f2df81e286a",
+        name = "remote_java_tools_windows",
+        sha256 = "36766802f7ec684cecb1a14c122428de6be9784e88419e2ab5912ad4b59a8c7d",
         urls = [
-            "https://mirror.bazel.build/bazel_java_tools/releases/javac9/v3.0/java_tools_javac9_darwin-v3.0.zip",
+            "https://mirror.bazel.build/bazel_java_tools/releases/java/v11.5/java_tools_windows-v11.5.zip",
+            "https://github.com/bazelbuild/java_tools/releases/download/java_v11.5/java_tools_windows-v11.5.zip",
         ],
     )
 
-def java_tools_javac10_repos():
     maybe(
         http_archive,
-        name = "remote_java_tools_javac10_linux",
-        sha256 = "52e03d400d978e9af6321786cdf477694c3838d7e78c2e5b926d0244670b6d3c",
+        name = "remote_java_tools_darwin",
+        sha256 = "792bc1352e736073b152528175ed424687f86a9f6f5f461f07d8b26806762738",
         urls = [
-            "https://mirror.bazel.build/bazel_java_tools/releases/javac10/v5.0/java_tools_javac10_linux-v5.0.zip",
+            "https://mirror.bazel.build/bazel_java_tools/releases/java/v11.5/java_tools_darwin-v11.5.zip",
+            "https://github.com/bazelbuild/java_tools/releases/download/java_v11.5/java_tools_darwin-v11.5.zip",
         ],
     )
+
+def local_jdk_repo():
     maybe(
-        http_archive,
-        name = "remote_java_tools_javac10_windows",
-        sha256 = "2e3fa82f5790917b56cec5f5d389ed5ff9592a00b5d66750a1f2b6387921d8be",
-        urls = [
-            "https://mirror.bazel.build/bazel_java_tools/releases/javac10/v5.0/java_tools_javac10_windows-v5.0.zip",
-        ],
+        local_java_repository,
+        name = "local_jdk",
+        build_file = Label("//toolchains:jdk.BUILD"),
     )
+
+def remote_jdk8_repos(name = ""):
+    """Imports OpenJDK 8 repositories.
+
+    Args:
+        name: The name of this macro (not used)
+    """
     maybe(
-        http_archive,
-        name = "remote_java_tools_javac10_macos",
-        sha256 = "d5503cc1700b3d544444302617ccc9b2c2780b7fa7bd013215da403148958c35",
+        remote_java_repository,
+        name = "remote_jdk8_linux_aarch64",
+        target_compatible_with = [
+            "@platforms//os:linux",
+            "@platforms//cpu:aarch64",
+        ],
+        sha256 = "f4072e82faa5a09fab2accf2892d4684324fc999d614583c3ff785e87c03963f",
+        strip_prefix = "zulu8.50.51.263-ca-jdk8.0.275-linux_aarch64",
         urls = [
-            "https://mirror.bazel.build/bazel_java_tools/releases/javac10/v5.0/java_tools_javac10_darwin-v5.0.zip",
+            "https://mirror.bazel.build/openjdk/azul-zulu-8.50.0.51-ca-jdk8.0.275/zulu8.50.51.263-ca-jdk8.0.275-linux_aarch64.tar.gz",
+            "https://cdn.azul.com/zulu-embedded/bin/zulu8.50.51.263-ca-jdk8.0.275-linux_aarch64.tar.gz",
         ],
+        version = "8",
     )
-
-def java_tools_javac11_repos():
     maybe(
-        http_archive,
-        name = "remote_java_tools_javac11_linux",
-        sha256 = "96e223094a12c842a66db0bb7bb6866e88e26e678f045842911f9bd6b47161f5",
+        remote_java_repository,
+        name = "remote_jdk8_linux",
+        target_compatible_with = [
+            "@platforms//os:linux",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "1db6b2fa642950ee1b4b1ec2b6bc8a9113d7a4cd723f79398e1ada7dab1c981c",
+        strip_prefix = "zulu8.50.0.51-ca-jdk8.0.275-linux_x64",
         urls = [
-            "https://mirror.bazel.build/bazel_java_tools/releases/javac11/v4.0/java_tools_javac11_linux-v4.0.zip",
+            "https://mirror.bazel.build/openjdk/azul-zulu-8.50.0.51-ca-jdk8.0.275/zulu8.50.0.51-ca-jdk8.0.275-linux_x64.tar.gz",
+            "https://cdn.azul.com/zulu/bin/zulu8.50.0.51-ca-jdk8.0.275-linux_x64.tar.gz",
         ],
+        version = "8",
     )
     maybe(
-        http_archive,
-        name = "remote_java_tools_javac11_windows",
-        sha256 = "a1de51447b2ba2eab923d589ba6c72c289c16e6091e6a3bb3e67a05ef4ad200c",
+        remote_java_repository,
+        name = "remote_jdk8_macos",
+        target_compatible_with = [
+            "@platforms//os:macos",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "b03176597734299c9a15b7c2cc770783cf14d121196196c1248e80c026b59c17",
+        strip_prefix = "zulu8.50.0.51-ca-jdk8.0.275-macosx_x64",
         urls = [
-            "https://mirror.bazel.build/bazel_java_tools/releases/javac11/v4.0/java_tools_javac11_windows-v4.0.zip",
+            "https://mirror.bazel.build/openjdk/azul-zulu-8.50.0.51-ca-jdk8.0.275/zulu8.50.0.51-ca-jdk8.0.275-macosx_x64.tar.gz",
+            "https://cdn.azul.com/zulu/bin/zulu8.50.0.51-ca-jdk8.0.275-macosx_x64.tar.gz",
         ],
+        version = "8",
     )
     maybe(
-        http_archive,
-        name = "remote_java_tools_javac11_macos",
-        sha256 = "fbf5bf22e9aab9c622e4c8c59314a1eef5ea09eafc5672b4f3250dc0b971bbcc",
+        remote_java_repository,
+        name = "remote_jdk8_windows",
+        target_compatible_with = [
+            "@platforms//os:windows",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "49759b2bd2ab28231a21ff3a3bb45824ddef55d89b5b1a05a62e26a365da0774",
+        strip_prefix = "zulu8.50.0.51-ca-jdk8.0.275-win_x64",
         urls = [
-            "https://mirror.bazel.build/bazel_java_tools/releases/javac11/v4.0/java_tools_javac11_darwin-v4.0.zip",
+            "https://mirror.bazel.build/openjdk/azul-zulu-8.50.0.51-ca-jdk8.0.275/zulu8.50.0.51-ca-jdk8.0.275-win_x64.zip",
+            "https://cdn.azul.com/zulu/bin/zulu8.50.0.51-ca-jdk8.0.275-win_x64.zip",
         ],
+        version = "8",
     )
+    REMOTE_JDK8_REPOS = [
+        "remote_jdk8_linux_aarch64",
+        "remote_jdk8_linux",
+        "remote_jdk8_macos",
+        "remote_jdk8_windows",
+    ]
+    for name in REMOTE_JDK8_REPOS:
+        native.register_toolchains("@" + name + "_toolchain_config_repo//:toolchain")
 
-def java_tools_javac12_repos():
+def remote_jdk11_repos():
+    """Imports OpenJDK 11 repositories."""
     maybe(
-        http_archive,
-        name = "remote_java_tools_javac12_linux",
-        sha256 = "fc199be2c7873b0792e00743679fedc1d249fa779c3fe7676111f8d7ced9f2b4",
-        urls = ["https://mirror.bazel.build/bazel_java_tools/releases/javac12/v2.0/java_tools_javac12_linux-v2.0.zip"],
+        remote_java_repository,
+        name = "remotejdk11_linux",
+        target_compatible_with = [
+            "@platforms//os:linux",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "b8e8a63b79bc312aa90f3558edbea59e71495ef1a9c340e38900dd28a1c579f3",
+        strip_prefix = "zulu11.50.19-ca-jdk11.0.12-linux_x64",
+        urls = [
+            "https://mirror.bazel.build/openjdk/azul-zulu11.50.19-ca-jdk11.0.12/zulu11.50.19-ca-jdk11.0.12-linux_x64.tar.gz",
+        ],
+        version = "11",
     )
+
     maybe(
-        http_archive,
-        name = "remote_java_tools_javac12_windows",
-        sha256 = "cab191830609838e99c9adc5e9628e8c839305674c5a9ecf1eea4ba0f6c0b0aa",
+        remote_java_repository,
+        name = "remotejdk11_linux_aarch64",
+        target_compatible_with = [
+            "@platforms//os:linux",
+            "@platforms//cpu:aarch64",
+        ],
+        sha256 = "61254688067454d3ccf0ef25993b5dcab7b56c8129e53b73566c28a8dd4d48fb",
+        strip_prefix = "zulu11.50.19-ca-jdk11.0.12-linux_aarch64",
         urls = [
-            "https://mirror.bazel.build/bazel_java_tools/releases/javac12/v2.0/java_tools_javac12_windows-v2.0.zip",
+            "https://mirror.bazel.build/openjdk/azul-zulu11.50.19-ca-jdk11.0.12/zulu11.50.19-ca-jdk11.0.12-linux_aarch64.tar.gz",
         ],
+        version = "11",
     )
+
     maybe(
-        http_archive,
-        name = "remote_java_tools_javac12_macos",
-        sha256 = "d73ff1de1fc2d3ea8403d54099dd2247a2a87390107e7cf81e3a383b0c687341",
+        remote_java_repository,
+        name = "remotejdk11_linux_ppc64le",
+        target_compatible_with = [
+            "@platforms//os:linux",
+            "@platforms//cpu:ppc",
+        ],
+        sha256 = "a417db0295b1f4b538ecbaf7c774f3a177fab9657a665940170936c0eca4e71a",
+        strip_prefix = "jdk-11.0.7+10",
         urls = [
-            "https://mirror.bazel.build/bazel_java_tools/releases/javac12/v2.0/java_tools_javac12_darwin-v2.0.zip",
+            "https://mirror.bazel.build/openjdk/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.7+10/OpenJDK11U-jdk_ppc64le_linux_hotspot_11.0.7_10.tar.gz",
+            "https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.7+10/OpenJDK11U-jdk_ppc64le_linux_hotspot_11.0.7_10.tar.gz",
         ],
+        version = "11",
     )
 
-def remote_jdk9_repos():
-    # OpenJDK distributions that should only be downloaded on demand (e.g. when
-    # building a java_library or a genrule that uses java make variables).
-    # This will allow us to stop bundling the full JDK with Bazel.
-    # Note that while these are currently the same as the openjdk_* rules in
-    # Bazel's WORKSPACE file, but they don't have to be the same.
-
-    # The source-code for this OpenJDK can be found at:
-    # https://openjdk.linaro.org/releases/jdk9-src-1708.tar.xz
     maybe(
-        http_archive,
-        name = "remote_jdk9_linux_aarch64",
-        build_file = "@local_jdk//:BUILD.bazel",
-        sha256 = "72e7843902b0395e2d30e1e9ad2a5f05f36a4bc62529828bcbc698d54aec6022",
-        strip_prefix = "jdk9-server-release-1708",
+        remote_java_repository,
+        name = "remotejdk11_linux_s390x",
+        target_compatible_with = [
+            "@platforms//os:linux",
+            "@platforms//cpu:s390x",
+        ],
+        sha256 = "d9b72e87a1d3ebc0c9552f72ae5eb150fffc0298a7cb841f1ce7bfc70dcd1059",
+        strip_prefix = "jdk-11.0.7+10",
         urls = [
-            # When you update this, also update the link to the source-code above.
-            "https://mirror.bazel.build/openjdk.linaro.org/releases/jdk9-server-release-170bazel_skylib8.tar.xz",
-            "http://openjdk.linaro.org/releases/jdk9-server-release-1708.tar.xz",
+            "https://mirror.bazel.build/github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.7+10/OpenJDK11U-jdk_s390x_linux_hotspot_11.0.7_10.tar.gz",
+            "https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.7+10/OpenJDK11U-jdk_s390x_linux_hotspot_11.0.7_10.tar.gz",
         ],
+        version = "11",
     )
 
     maybe(
-        http_archive,
-        name = "remote_jdk9_linux",
-        build_file_content = "java_runtime(name = 'runtime', srcs =  glob(['**']), visibility = ['//visibility:public'])",
-        sha256 = "45f2dfbee93b91b1468cf81d843fc6d9a47fef1f831c0b7ceff4f1eb6e6851c8",
-        strip_prefix = "zulu9.0.7.1-jdk9.0.7-linux_x64",
+        remote_java_repository,
+        name = "remotejdk11_macos",
+        target_compatible_with = [
+            "@platforms//os:macos",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "0b8c8b7cf89c7c55b7e2239b47201d704e8d2170884875b00f3103cf0662d6d7",
+        strip_prefix = "zulu11.50.19-ca-jdk11.0.12-macosx_x64",
         urls = [
-            "https://mirror.bazel.build/openjdk/azul-zulu-9.0.7.1-jdk9.0.7/zulu9.0.7.1-jdk9.0.7-linux_x64.tar.gz",
+            "https://mirror.bazel.build/openjdk/azul-zulu11.50.19-ca-jdk11.0.12/zulu11.50.19-ca-jdk11.0.12-macosx_x64.tar.gz",
         ],
+        version = "11",
     )
+
     maybe(
-        http_archive,
-        name = "remote_jdk9_macos",
-        build_file_content = "java_runtime(name = 'runtime', srcs =  glob(['**']), visibility = ['//visibility:public'])",
-        strip_prefix = "zulu9.0.7.1-jdk9.0.7-macosx_x64",
+        remote_java_repository,
+        name = "remotejdk11_macos_aarch64",
+        target_compatible_with = [
+            "@platforms//os:macos",
+            "@platforms//cpu:aarch64",
+        ],
+        sha256 = "e908a0b4c0da08d41c3e19230f819b364ff2e5f1dafd62d2cf991a85a34d3a17",
+        strip_prefix = "zulu11.50.19-ca-jdk11.0.12-macosx_aarch64",
         urls = [
-            "https://mirror.bazel.build/openjdk/azul-zulu-9.0.7.1-jdk9.0.7/zulu9.0.7.1-jdk9.0.7-macosx_x64.tar.gz",
+            "https://mirror.bazel.build/openjdk/azul-zulu11.50.19-ca-jdk11.0.12/zulu11.50.19-ca-jdk11.0.12-macosx_aarch64.tar.gz",
+            "https://cdn.azul.com/zulu/bin/zulu11.50.19-ca-jdk11.0.12-macosx_aarch64.tar.gz",
         ],
+        version = "11",
     )
+
     maybe(
-        http_archive,
-        name = "remote_jdk9_windows",
-        build_file_content = "java_runtime(name = 'runtime', srcs =  glob(['**']), visibility = ['//visibility:public'])",
-        strip_prefix = "zulu9.0.7.1-jdk9.0.7-win_x64",
+        remote_java_repository,
+        name = "remotejdk11_win",
+        target_compatible_with = [
+            "@platforms//os:windows",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "42ae65e75d615a3f06a674978e1fa85fdf078cad94e553fee3e779b2b42bb015",
+        strip_prefix = "zulu11.50.19-ca-jdk11.0.12-win_x64",
         urls = [
-            "https://mirror.bazel.build/openjdk/azul-zulu-9.0.7.1-jdk9.0.7/zulu9.0.7.1-jdk9.0.7-win_x64.zip",
+            "https://mirror.bazel.build/openjdk/azul-zulu11.50.19-ca-jdk11.0.12/zulu11.50.19-ca-jdk11.0.12-win_x64.zip",
         ],
+        version = "11",
     )
 
-def remote_jdk10_repos():
-    # The source-code for this OpenJDK can be found at:
-    # https://openjdk.linaro.org/releases/jdk10-src-1804.tar.xz
+def remote_jdk15_repos():
+    """Imports OpenJDK 15 repositories."""
     maybe(
-        http_archive,
-        name = "remote_jdk10_linux_aarch64",
-        build_file = "@local_jdk//:BUILD.bazel",
-        sha256 = "b7098b7aaf6ee1ffd4a2d0371a0be26c5a5c87f6aebbe46fe9a92c90583a84be",
-        strip_prefix = "jdk10-server-release-1804",
+        remote_java_repository,
+        name = "remotejdk15_linux",
+        target_compatible_with = [
+            "@platforms//os:linux",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "0a38f1138c15a4f243b75eb82f8ef40855afcc402e3c2a6de97ce8235011b1ad",
+        strip_prefix = "zulu15.27.17-ca-jdk15.0.0-linux_x64",
         urls = [
-            # When you update this, also update the link to the source-code above.
-            "https://mirror.bazel.build/openjdk.linaro.org/releases/jdk10-server-release-1804.tar.xz",
-            "http://openjdk.linaro.org/releases/jdk10-server-release-1804.tar.xz",
+            "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-linux_x64.tar.gz",
+            "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-linux_x64.tar.gz",
         ],
+        version = "15",
     )
+
     maybe(
-        http_archive,
-        name = "remote_jdk10_linux",
-        build_file_content = "java_runtime(name = 'runtime', srcs =  glob(['**']), visibility = ['//visibility:public'])",
-        sha256 = "b3c2d762091a615b0c1424ebbd05d75cc114da3bf4f25a0dec5c51ea7e84146f",
-        strip_prefix = "zulu10.2+3-jdk10.0.1-linux_x64",
+        remote_java_repository,
+        name = "remotejdk15_macos",
+        target_compatible_with = [
+            "@platforms//os:macos",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "f80b2e0512d9d8a92be24497334c974bfecc8c898fc215ce0e76594f00437482",
+        strip_prefix = "zulu15.27.17-ca-jdk15.0.0-macosx_x64",
         urls = [
-            "https://mirror.bazel.build/openjdk/azul-zulu10.2+3-jdk10.0.1/zulu10.2+3-jdk10.0.1-linux_x64.tar.gz",
+            "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-macosx_x64.tar.gz",
+            "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-macosx_x64.tar.gz",
         ],
+        version = "15",
     )
+
     maybe(
-        http_archive,
-        name = "remote_jdk10_macos",
-        build_file_content = "java_runtime(name = 'runtime', srcs =  glob(['**']), visibility = ['//visibility:public'])",
-        strip_prefix = "zulu10.2+3-jdk10.0.1-macosx_x64",
+        remote_java_repository,
+        name = "remotejdk15_macos_aarch64",
+        target_compatible_with = [
+            "@platforms//os:macos",
+            "@platforms//cpu:aarch64",
+        ],
+        sha256 = "2613c3f15eef6b6ecd0fd102da92282b985e4573905dc902f1783d8059c1efc5",
+        strip_prefix = "zulu15.29.15-ca-jdk15.0.2-macosx_aarch64",
         urls = [
-            "https://mirror.bazel.build/openjdk/azul-zulu10.2+3-jdk10.0.1/zulu10.2+3-jdk10.0.1-macosx_x64.tar.gz",
+            "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu15.29.15-ca-jdk15.0.2-macosx_aarch64.tar.gz",
+            "https://cdn.azul.com/zulu/bin/zulu15.29.15-ca-jdk15.0.2-macosx_aarch64.tar.gz",
         ],
+        version = "15",
     )
+
     maybe(
-        http_archive,
-        name = "remote_jdk10_windows",
-        build_file_content = "java_runtime(name = 'runtime', srcs =  glob(['**']), visibility = ['//visibility:public'])",
-        strip_prefix = "zulu10.2+3-jdk10.0.1-win_x64",
+        remote_java_repository,
+        name = "remotejdk15_win",
+        target_compatible_with = [
+            "@platforms//os:windows",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "f535a530151e6c20de8a3078057e332b08887cb3ba1a4735717357e72765cad6",
+        strip_prefix = "zulu15.27.17-ca-jdk15.0.0-win_x64",
         urls = [
-            "https://mirror.bazel.build/openjdk/azul-zulu10.2+3-jdk10.0.1/zulu10.2+3-jdk10.0.1-win_x64.zip",
+            "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-win_x64.zip",
+            "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-win_x64.zip",
         ],
+        version = "15",
     )
 
-def remote_jdk11_repos():
-    # The source-code for this OpenJDK can be found at:
-    # https://openjdk.linaro.org/releases/jdk10-src-1804.tar.xz
+def remote_jdk16_repos():
+    """Imports OpenJDK 16 repositories."""
     maybe(
-        http_archive,
-        name = "remote_jdk11_linux_aarch64",
-        build_file = "@local_jdk//:BUILD.bazel",
-        sha256 = "3b0d91611b1bdc4d409afcf9eab4f0e7f4ae09f88fc01bd9f2b48954882ae69b",
-        strip_prefix = "zulu11.31.15-ca-jdk11.0.3-linux_aarch64",
+        remote_java_repository,
+        name = "remotejdk16_linux",
+        target_compatible_with = [
+            "@platforms//os:linux",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "236b5ea97aff3cb312e743848d7efa77faf305170e41371a732ca93c1b797665",
+        strip_prefix = "zulu16.28.11-ca-jdk16.0.0-linux_x64",
         urls = [
-            "https://mirror.bazel.build/openjdk/azul-zulu11.31.15-ca-jdk11.0.3/zulu11.31.15-ca-jdk11.0.3-linux_aarch64.tar.gz",
+            "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu16.28.11-ca-jdk16.0.0-linux_x64.tar.gz",
+            "https://cdn.azul.com/zulu/bin/zulu16.28.11-ca-jdk16.0.0-linux_x64.tar.gz",
         ],
+        version = "16",
     )
+
     maybe(
-        http_archive,
-        "remote_jdk11_linux",
-        build_file = "@local_jdk//:BUILD.bazel",
-        sha256 = "232b1c3511f0d26e92582b7c3cc363be7ac633e371854ca2f2e9f2b50eb72a75",
-        strip_prefix = "zulu11.2.3-jdk11.0.1-linux_x64",
+        remote_java_repository,
+        name = "remotejdk16_macos",
+        target_compatible_with = [
+            "@platforms//os:macos",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "6d47ef22dc56ce1f5a102ed39e21d9a97320f0bb786818e2c686393109d79bc5",
+        strip_prefix = "zulu16.28.11-ca-jdk16.0.0-macosx_x64",
         urls = [
-            "https://mirror.bazel.build/openjdk/azul-zulu11.2.3-jdk11.0.1/zulu11.2.3-jdk11.0.1-linux_x64.tar.gz",
+            "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu16.28.11-ca-jdk16.0.0-macosx_x64.tar.gz",
+            "https://cdn.azul.com/zulu/bin/zulu16.28.11-ca-jdk16.0.0-macosx_x64.tar.gz",
         ],
+        version = "16",
     )
 
     maybe(
-        http_archive,
-        "remote_jdk11_macos",
-        build_file = "@local_jdk//:BUILD.bazel",
-        sha256 = "1edf366ee821e5db8e348152fcb337b28dfd6bf0f97943c270dcc6747cedb6cb",
-        strip_prefix = "zulu11.2.3-jdk11.0.1-macosx_x64",
+        remote_java_repository,
+        name = "remotejdk16_macos_aarch64",
+        target_compatible_with = [
+            "@platforms//os:macos",
+            "@platforms//cpu:aarch64",
+        ],
+        sha256 = "c92131e83bc71474850e667bc4e05fca33662b8feb009a0547aa14e76b40e890",
+        strip_prefix = "zulu16.28.11-ca-jdk16.0.0-macosx_aarch64",
         urls = [
-            "https://mirror.bazel.build/openjdk/azul-zulu11.2.3-jdk11.0.1/zulu11.2.3-jdk11.0.1-macosx_x64.tar.gz",
+            "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu16.28.11-ca-jdk16.0.0-macosx_aarch64.tar.gz",
+            "https://cdn.azul.com/zulu/bin/zulu16.28.11-ca-jdk16.0.0-macosx_aarch64.tar.gz",
         ],
+        version = "16",
     )
 
     maybe(
-        http_archive,
-        "remote_jdk11_windows",
-        build_file = "@local_jdk//:BUILD.bazel",
-        sha256 = "8e1e2b8347de6746f3fd1538840dd643201533ab113abc4ed93678e342d28aa3",
-        strip_prefix = "zulu11.2.3-jdk11.0.1-win_x64",
+        remote_java_repository,
+        name = "remotejdk16_win",
+        target_compatible_with = [
+            "@platforms//os:windows",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "6cbf98ada27476526a5f6dff79fd5f2c15e2f671818e503bdf741eb6c8fed3d4",
+        strip_prefix = "zulu16.28.11-ca-jdk16.0.0-win_x64",
         urls = [
-            "https://mirror.bazel.build/openjdk/azul-zulu11.2.3-jdk11.0.1/zulu11.2.3-jdk11.0.1-win_x64.zip",
+            "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu16.28.11-ca-jdk16.0.0-win_x64.zip",
+            "https://cdn.azul.com/zulu/bin/zulu16.28.11-ca-jdk16.0.0-win_x64.zip",
         ],
+        version = "16",
     )
 
-def remote_jdk12_repos():
+def remote_jdk17_repos():
+    """Imports OpenJDK 17 repositories."""
     maybe(
-        http_archive,
-        name = "remote_jdk12_linux",
-        build_file_content = "java_runtime(name = 'runtime', srcs =  glob(['**']), visibility = ['//visibility:public'])",
-        strip_prefix = "zulu12.2.3-ca-jdk12.0.1-linux_x64",
+        remote_java_repository,
+        name = "remotejdk17_linux",
+        target_compatible_with = [
+            "@platforms//os:linux",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "37c4f8e48536cceae8c6c20250d6c385e176972532fd35759fa7d6015c965f56",
+        strip_prefix = "zulu17.28.13-ca-jdk17.0.0-linux_x64",
         urls = [
-            "https://mirror.bazel.build/openjdk/azul-zulu12.2.3-ca-jdk12.0.1/zulu12.2.3-ca-jdk12.0.1-linux_x64.tar.gz",
+            "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.28.13-ca-jdk17.0.0-linux_x64.tar.gz",
+            "https://cdn.azul.com/zulu/bin/zulu17.28.13-ca-jdk17.0.0-linux_x64.tar.gz",
         ],
+        version = "17",
     )
+
     maybe(
-        http_archive,
-        name = "remote_jdk12_macos",
-        build_file_content = "java_runtime(name = 'runtime', srcs =  glob(['**']), visibility = ['//visibility:public'])",
-        strip_prefix = "zulu12.2.3-ca-jdk12.0.1-macosx_x64",
+        remote_java_repository,
+        name = "remotejdk17_macos",
+        target_compatible_with = [
+            "@platforms//os:macos",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "6029b1fe6853cecad22ab99ac0b3bb4fb8c903dd2edefa91c3abc89755bbd47d",
+        strip_prefix = "zulu17.28.13-ca-jdk17.0.0-macosx_x64",
         urls = [
-            "https://mirror.bazel.build/openjdk/azul-zulu12.2.3-ca-jdk12.0.1/zulu12.2.3-ca-jdk12.0.1-macosx_x64.tar.gz",
+            "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.28.13-ca-jdk17.0.0-macosx_x64.tar.gz",
+            "https://cdn.azul.com/zulu/bin/zulu17.28.13-ca-jdk17.0.0-macosx_x64.tar.gz",
         ],
+        version = "17",
     )
+
     maybe(
-        http_archive,
-        name = "remote_jdk12_windows",
-        build_file_content = "java_runtime(name = 'runtime', srcs =  glob(['**']), visibility = ['//visibility:public'])",
-        strip_prefix = "zulu12.2.3-ca-jdk12.0.1-win_x64",
+        remote_java_repository,
+        name = "remotejdk17_macos_aarch64",
+        target_compatible_with = [
+            "@platforms//os:macos",
+            "@platforms//cpu:aarch64",
+        ],
+        sha256 = "6b17f01f767ee7abf4704149ca4d86423aab9b16b68697b7d36e9b616846a8b0",
+        strip_prefix = "zulu17.28.13-ca-jdk17.0.0-macosx_aarch64",
         urls = [
-            "https://mirror.bazel.build/openjdk/azul-zulu12.2.3-ca-jdk12.0.1/zulu12.2.3-ca-jdk12.0.1-win_x64.zip",
+            "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.28.13-ca-jdk17.0.0-macosx_aarch64.tar.gz",
+            "https://cdn.azul.com/zulu/bin/zulu17.28.13-ca-jdk17.0.0-macosx_aarch64.tar.gz",
         ],
+        version = "17",
     )
 
-def bazel_skylib():
     maybe(
-        http_archive,
-        name = "bazel_skylib",
-        type = "tar.gz",
-        url = "https://github.com/bazelbuild/bazel-skylib/releases/download/0.9.0/bazel_skylib-0.9.0.tar.gz",
-        sha256 = "1dde365491125a3db70731e25658dfdd3bc5dbdfd11b840b3e987ecf043c7ca0",
+        remote_java_repository,
+        name = "remotejdk17_win",
+        target_compatible_with = [
+            "@platforms//os:windows",
+            "@platforms//cpu:x86_64",
+        ],
+        sha256 = "f4437011239f3f0031c794bb91c02a6350bc941d4196bdd19c9f157b491815a3",
+        strip_prefix = "zulu17.28.13-ca-jdk17.0.0-win_x64",
+        urls = [
+            "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.28.13-ca-jdk17.0.0-win_x64.zip",
+            "https://cdn.azul.com/zulu/bin/zulu17.28.13-ca-jdk17.0.0-win_x64.zip",
+        ],
+        version = "17",
     )
 
 def rules_java_dependencies():
@@ -311,13 +465,35 @@ def rules_java_dependencies():
     Loads the remote repositories used by default in Bazel.
     """
 
+    local_jdk_repo()
     remote_jdk11_repos()
-    java_tools_javac11_repos()
-    bazel_skylib()
+    remote_jdk15_repos()
+    remote_jdk16_repos()
+    remote_jdk17_repos()
 
-def rules_java_toolchains():
+    # TODO: load this will break compatibility with Bazel 4.2.1,
+    # Enable this when Bazel 5.0.0 is released.
+    # java_tools_repos()
+
+def rules_java_toolchains(name = "toolchains"):
     """An utility method to load all Java toolchains.
 
-    It doesn't do anything at the moment.
+    Args:
+        name: The name of this macro (not used)
     """
-    pass
+    JDK_VERSIONS = ["11", "15", "16", "17"]
+    PLATFORMS = ["linux", "macos", "macos_aarch64", "win"]
+
+    # Remote JDK repos for those Linux platforms are only defined for JDK 11.
+    EXTRA_REMOTE_JDK11_REPOS = [
+        "remotejdk11_linux_aarch64",
+        "remotejdk11_linux_ppc64le",
+        "remotejdk11_linux_s390x",
+    ]
+
+    REMOTE_JDK_REPOS = [("remotejdk" + version + "_" + platform) for version in JDK_VERSIONS for platform in PLATFORMS] + EXTRA_REMOTE_JDK11_REPOS
+
+    native.register_toolchains("//toolchains:all")
+    native.register_toolchains("@local_jdk//:runtime_toolchain_definition")
+    for name in REMOTE_JDK_REPOS:
+        native.register_toolchains("@" + name + "_toolchain_config_repo//:toolchain")
diff --git a/renovate.json b/renovate.json
new file mode 100644
index 0000000..ee8c906
--- /dev/null
+++ b/renovate.json
@@ -0,0 +1,5 @@
+{
+    "extends": [
+        "config:base"
+    ]
+}
diff --git a/toolchains/BUILD b/toolchains/BUILD
new file mode 100644
index 0000000..3c1f8b2
--- /dev/null
+++ b/toolchains/BUILD
@@ -0,0 +1,277 @@
+load("@rules_cc//cc:defs.bzl", "cc_library")
+load(
+    "//toolchains:default_java_toolchain.bzl",
+    "DEFAULT_TOOLCHAIN_CONFIGURATION",
+    "PREBUILT_TOOLCHAIN_CONFIGURATION",
+    "bootclasspath",
+    "default_java_toolchain",
+    "java_runtime_files",
+)
+load(
+    "//toolchains:java_toolchain_alias.bzl",
+    "java_host_runtime_alias",
+    "java_runtime_alias",
+    "java_runtime_version_alias",
+    "java_toolchain_alias",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])
+
+filegroup(
+    name = "srcs",
+    srcs = glob(["**"]),
+    visibility = ["@//:__pkg__"],
+)
+
+# Used to distinguish toolchains used for Java development, ie the JavaToolchainProvider.
+# TODO: migrate away from using @bazel_tools//tools/jdk:toolchain_type ?
+# toolchain_type(name = "toolchain_type")
+
+# Used to distinguish toolchains used for Java execution, ie the JavaRuntimeInfo.
+# TODO: migrate away from using @bazel_tools//tools/jdk:runtime_toolchain_type ?
+# toolchain_type(name = "runtime_toolchain_type")
+
+# Points to toolchain[":runtime_toolchain_type"] (was :legacy_current_java_runtime)
+java_runtime_alias(name = "current_java_runtime")
+
+# Host configuration of ":current_java_runtime"
+java_host_runtime_alias(name = "current_host_java_runtime")
+
+# Points to toolchain[":toolchain_type"] (was :legacy_current_java_toolchain)
+java_toolchain_alias(name = "current_java_toolchain")
+
+# These individual jni_* targets are exposed for legacy reasons.
+# Most users should depend on :jni.
+
+java_runtime_files(
+    name = "jni_header",
+    srcs = ["include/jni.h"],
+)
+
+java_runtime_files(
+    name = "jni_md_header-darwin",
+    srcs = ["include/darwin/jni_md.h"],
+)
+
+java_runtime_files(
+    name = "jni_md_header-linux",
+    srcs = ["include/linux/jni_md.h"],
+)
+
+java_runtime_files(
+    name = "jni_md_header-windows",
+    srcs = ["include/win32/jni_md.h"],
+)
+
+java_runtime_files(
+    name = "jni_md_header-freebsd",
+    srcs = ["include/freebsd/jni_md.h"],
+)
+
+java_runtime_files(
+    name = "jni_md_header-openbsd",
+    srcs = ["include/openbsd/jni_md.h"],
+)
+
+# The Java native interface. Depend on this package if you #include <jni.h>.
+#
+# See test_jni in third_party/bazel/src/test/shell/bazel/bazel_java_test.sh for
+# an example of using Bazel to build a Java program that calls a C function.
+#
+# TODO(ilist): use //src:condition:linux when released in Bazel
+cc_library(
+    name = "jni",
+    hdrs = [":jni_header"] + select({
+        "@bazel_tools//src/conditions:darwin": [":jni_md_header-darwin"],
+        "@bazel_tools//src/conditions:freebsd": [":jni_md_header-freebsd"],
+        "@bazel_tools//src/conditions:linux_aarch64": [":jni_md_header-linux"],
+        "@bazel_tools//src/conditions:linux_mips64": [":jni_md_header-linux"],
+        "@bazel_tools//src/conditions:linux_ppc64le": [":jni_md_header-linux"],
+        "@bazel_tools//src/conditions:linux_riscv64": [":jni_md_header-linux"],
+        "@bazel_tools//src/conditions:linux_s390x": [":jni_md_header-linux"],
+        "@bazel_tools//src/conditions:linux_x86_64": [":jni_md_header-linux"],
+        "@bazel_tools//src/conditions:openbsd": [":jni_md_header-openbsd"],
+        "@bazel_tools//src/conditions:windows": [":jni_md_header-windows"],
+        "//conditions:default": [],
+    }),
+    includes = ["include"] + select({
+        "@bazel_tools//src/conditions:darwin": ["include/darwin"],
+        "@bazel_tools//src/conditions:freebsd": ["include/freebsd"],
+        "@bazel_tools//src/conditions:linux_aarch64": ["include/linux"],
+        "@bazel_tools//src/conditions:linux_mips64": [":include/linux"],
+        "@bazel_tools//src/conditions:linux_ppc64le": ["include/linux"],
+        "@bazel_tools//src/conditions:linux_riscv64": [":include/linux"],
+        "@bazel_tools//src/conditions:linux_s390x": ["include/linux"],
+        "@bazel_tools//src/conditions:linux_x86_64": ["include/linux"],
+        "@bazel_tools//src/conditions:openbsd": ["include/openbsd"],
+        "@bazel_tools//src/conditions:windows": ["include/win32"],
+        "//conditions:default": [],
+    }),
+)
+
+[
+    (
+        alias(
+            name = "ijar_prebuilt_binary_%s" % OS,
+            actual = "@remote_java_tools_%s//:ijar_prebuilt_binary" % OS,
+            visibility = ["//visibility:private"],
+        ),
+        alias(
+            name = "prebuilt_singlejar_%s" % OS,
+            actual = "@remote_java_tools_%s//:prebuilt_singlejar" % OS,
+            visibility = ["//visibility:private"],
+        ),
+    )
+    for OS in [
+        "linux",
+        "darwin",
+        "windows",
+    ]
+]
+
+# On Windows, executables end in ".exe", but the label we reach it through
+# must be platform-independent. Thus, we create a little filegroup that
+# contains the appropriate platform-dependent file.
+alias(
+    name = "ijar",
+    actual = ":ijar_prebuilt_binary_or_cc_binary",
+)
+
+alias(
+    name = "ijar_prebuilt_binary_or_cc_binary",
+    actual = select({
+        "@bazel_tools//src/conditions:darwin": ":ijar_prebuilt_binary_darwin",
+        "@bazel_tools//src/conditions:linux_x86_64": ":ijar_prebuilt_binary_linux",
+        "@bazel_tools//src/conditions:windows": ":ijar_prebuilt_binary_windows",
+        "//conditions:default": "@remote_java_tools//:ijar_cc_binary",
+    }),
+)
+
+alias(
+    name = "ijar_prebuilt_binary",
+    actual = select({
+        "@bazel_tools//src/conditions:darwin": ":ijar_prebuilt_binary_darwin",
+        "@bazel_tools//src/conditions:linux_x86_64": ":ijar_prebuilt_binary_linux",
+        "@bazel_tools//src/conditions:windows": ":ijar_prebuilt_binary_windows",
+    }),
+)
+
+# On Windows, Java implementation of singlejar is used. We create a little
+# filegroup that contains the appropriate platform-dependent file.
+# Once https://github.com/bazelbuild/bazel/issues/2241 is fixed (that is,
+# the native singlejar is used on windows), this file group can be reused since
+# on Windows, executables end in ".exe", but the label we reach it through
+# must be platform-independent.
+alias(
+    name = "singlejar",
+    actual = ":singlejar_prebuilt_or_cc_binary",
+)
+
+alias(
+    name = "singlejar_prebuilt_or_cc_binary",
+    actual = select({
+        "@bazel_tools//src/conditions:darwin": ":prebuilt_singlejar_darwin",
+        "@bazel_tools//src/conditions:linux_x86_64": ":prebuilt_singlejar_linux",
+        "@bazel_tools//src/conditions:windows": ":prebuilt_singlejar_windows",
+        "//conditions:default": "@remote_java_tools//:singlejar_cc_bin",
+    }),
+)
+
+alias(
+    name = "prebuilt_singlejar",
+    actual = select({
+        "@bazel_tools//src/conditions:darwin": ":prebuilt_singlejar_darwin",
+        "@bazel_tools//src/conditions:linux_x86_64": ":prebuilt_singlejar_linux",
+        "@bazel_tools//src/conditions:windows": ":prebuilt_singlejar_windows",
+    }),
+)
+
+bootclasspath(
+    name = "platformclasspath",
+    src = "DumpPlatformClassPath.java",
+    host_javabase = "current_java_runtime",
+    target_javabase = "current_java_runtime",
+)
+
+default_java_toolchain(
+    name = "toolchain",
+    configuration = DEFAULT_TOOLCHAIN_CONFIGURATION,
+    toolchain_definition = False,
+)
+
+alias(
+    name = "remote_toolchain",
+    actual = ":toolchain",
+)
+
+RELEASES = (8, 9, 10, 11)
+
+[
+    default_java_toolchain(
+        name = "toolchain_java%d" % release,
+        configuration = DEFAULT_TOOLCHAIN_CONFIGURATION,
+        source_version = "%s" % release,
+        target_version = "%s" % release,
+    )
+    for release in RELEASES
+]
+
+# A toolchain that targets java 15.
+default_java_toolchain(
+    name = "toolchain_jdk_15",
+    configuration = dict(),
+    java_runtime = "//toolchains:remotejdk_15",
+    source_version = "15",
+    target_version = "15",
+)
+
+# A toolchain that targets java 16.
+default_java_toolchain(
+    name = "toolchain_jdk_16",
+    configuration = dict(),
+    java_runtime = "//toolchains:remotejdk_16",
+    source_version = "16",
+    target_version = "16",
+)
+
+# A toolchain that targets java 17.
+default_java_toolchain(
+    name = "toolchain_jdk_17",
+    configuration = dict(),
+    java_runtime = "//toolchains:remotejdk_17",
+    source_version = "17",
+    target_version = "17",
+)
+
+default_java_toolchain(
+    name = "prebuilt_toolchain",
+    configuration = PREBUILT_TOOLCHAIN_CONFIGURATION,
+    toolchain_definition = False,
+)
+
+# A JDK 11 for use as a --host_javabase.
+java_runtime_version_alias(
+    name = "remote_jdk11",
+    runtime_version = "remotejdk_11",
+    visibility = ["//visibility:public"],
+)
+
+java_runtime_version_alias(
+    name = "remotejdk_15",
+    runtime_version = "remotejdk_15",
+    visibility = ["//visibility:public"],
+)
+
+java_runtime_version_alias(
+    name = "remotejdk_16",
+    runtime_version = "remotejdk_16",
+    visibility = ["//visibility:public"],
+)
+
+java_runtime_version_alias(
+    name = "remotejdk_17",
+    runtime_version = "remotejdk_17",
+    visibility = ["//visibility:public"],
+)
diff --git a/toolchains/DumpPlatformClassPath.java b/toolchains/DumpPlatformClassPath.java
new file mode 100644
index 0000000..e23ca53
--- /dev/null
+++ b/toolchains/DumpPlatformClassPath.java
@@ -0,0 +1,259 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.util.Context;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+
+/**
+ * Output a jar file containing all classes on the platform classpath of the given JDK release.
+ *
+ * <p>usage: DumpPlatformClassPath <release version> <output jar> <path to target JDK>?
+ */
+public class DumpPlatformClassPath {
+
+  public static void main(String[] args) throws Exception {
+    if (args.length != 2) {
+      System.err.println("usage: DumpPlatformClassPath <output jar> <path to target JDK>");
+      System.exit(1);
+    }
+    Path output = Paths.get(args[0]);
+    Path targetJavabase = Paths.get(args[1]);
+
+    int hostMajorVersion = hostMajorVersion();
+    boolean ok;
+    if (hostMajorVersion == 8) {
+      ok = dumpJDK8BootClassPath(output, targetJavabase);
+    } else {
+      ok = dumpJDK9AndNewerBootClassPath(hostMajorVersion, output, targetJavabase);
+    }
+    System.exit(ok ? 0 : 1);
+  }
+
+  // JDK 8 bootclasspath handling.
+  // * JDK 8 represents a bootclasspath as a search path of jars (rt.jar, etc.).
+  // * It does not support --release or --system.
+  static boolean dumpJDK8BootClassPath(Path output, Path targetJavabase) throws IOException {
+    List<Path> bootClassPathJars = getBootClassPathJars(targetJavabase);
+    writeClassPathJars(output, bootClassPathJars);
+    return true;
+  }
+
+  // JDK > 8 --host_javabase bootclasspath handling.
+  // (The default --host_javabase is currently JDK 9.)
+  static boolean dumpJDK9AndNewerBootClassPath(
+      int hostMajorVersion, Path output, Path targetJavabase) throws IOException {
+
+    // JDK 9 and newer support cross-compiling to older platform versions using the --system
+    // and --release flags.
+    // * --system takes the path to a JDK root for JDK 9 and up, and causes the compilation
+    //     to target the APIs from that JDK.
+    // * --release takes a language level (e.g. '9') and uses the API information baked in to
+    //     the host JDK (in lib/ct.sym).
+
+    // Since --system only supports JDK >= 9, first check of the target JDK defines a JDK 8
+    // bootclasspath.
+    List<Path> bootClassPathJars = getBootClassPathJars(targetJavabase);
+    if (!bootClassPathJars.isEmpty()) {
+      writeClassPathJars(output, bootClassPathJars);
+      return true;
+    }
+
+    // Initialize a FileManager to process the --system argument, and then read the
+    // initialized bootclasspath data back out.
+
+    Context context = new Context();
+    JavacTool.create()
+        .getTask(
+            /* out = */ null,
+            /* fileManager = */ null,
+            /* diagnosticListener = */ null,
+            /* options = */ Arrays.asList("--system", String.valueOf(targetJavabase)),
+            /* classes = */ null,
+            /* compilationUnits = */ null,
+            context);
+    StandardJavaFileManager fileManager =
+        (StandardJavaFileManager) context.get(JavaFileManager.class);
+
+    SortedMap<String, InputStream> entries = new TreeMap<>();
+    for (JavaFileObject fileObject :
+        fileManager.list(
+            StandardLocation.PLATFORM_CLASS_PATH,
+            "",
+            EnumSet.of(Kind.CLASS),
+            /* recurse= */ true)) {
+      String binaryName =
+          fileManager.inferBinaryName(StandardLocation.PLATFORM_CLASS_PATH, fileObject);
+      entries.put(binaryName.replace('.', '/') + ".class", fileObject.openInputStream());
+    }
+    writeEntries(output, entries);
+    return true;
+  }
+
+  /** Writes the given entry names and data to a jar archive at the given path. */
+  private static void writeEntries(Path output, Map<String, InputStream> entries)
+      throws IOException {
+    if (!entries.containsKey("java/lang/Object.class")) {
+      throw new AssertionError(
+          "\nCould not find java.lang.Object on bootclasspath; something has gone terribly wrong.\n"
+              + "Please file a bug: https://github.com/bazelbuild/bazel/issues");
+    }
+    try (OutputStream os = Files.newOutputStream(output);
+        BufferedOutputStream bos = new BufferedOutputStream(os, 65536);
+        JarOutputStream jos = new JarOutputStream(bos)) {
+      entries.entrySet().stream()
+          .forEachOrdered(
+              entry -> {
+                try {
+                  addEntry(jos, entry.getKey(), entry.getValue());
+                } catch (IOException e) {
+                  throw new UncheckedIOException(e);
+                }
+              });
+    }
+  }
+
+  /** Collects the entries of the given jar files into a map from jar entry names to their data. */
+  private static void writeClassPathJars(Path output, Collection<Path> paths) throws IOException {
+    List<JarFile> jars = new ArrayList<>();
+    for (Path path : paths) {
+      jars.add(new JarFile(path.toFile()));
+    }
+    SortedMap<String, InputStream> entries = new TreeMap<>();
+    for (JarFile jar : jars) {
+      jar.stream()
+          .filter(p -> p.getName().endsWith(".class"))
+          .forEachOrdered(
+              entry -> {
+                try {
+                  entries.put(entry.getName(), jar.getInputStream(entry));
+                } catch (IOException e) {
+                  throw new UncheckedIOException(e);
+                }
+              });
+    }
+    writeEntries(output, entries);
+    for (JarFile jar : jars) {
+      jar.close();
+    }
+  }
+
+  /** Returns paths to the entries of a JDK 8-style bootclasspath. */
+  private static List<Path> getBootClassPathJars(Path javaHome) throws IOException {
+    List<Path> jars = new ArrayList<>();
+    Path extDir = javaHome.resolve("jre/lib/ext");
+    if (Files.exists(extDir)) {
+      for (Path extJar : Files.newDirectoryStream(extDir, "*.jar")) {
+        jars.add(extJar);
+      }
+    }
+    for (String jar :
+        Arrays.asList("rt.jar", "resources.jar", "jsse.jar", "jce.jar", "charsets.jar")) {
+      Path path = javaHome.resolve("jre/lib").resolve(jar);
+      if (Files.exists(path)) {
+        jars.add(path);
+      }
+    }
+    return jars;
+  }
+
+  // Use a fixed timestamp for deterministic jar output.
+  private static final long FIXED_TIMESTAMP =
+      new GregorianCalendar(2010, 0, 1, 0, 0, 0).getTimeInMillis();
+
+  /**
+   * Add a jar entry to the given {@link JarOutputStream}, normalizing the entry timestamps to
+   * ensure deterministic build output.
+   */
+  private static void addEntry(JarOutputStream jos, String name, InputStream input)
+      throws IOException {
+    JarEntry je = new JarEntry(name);
+    je.setTime(FIXED_TIMESTAMP);
+    je.setMethod(ZipEntry.STORED);
+    byte[] bytes = toByteArray(input);
+    // When targeting JDK >= 10, patch the major version so it will be accepted by javac 9
+    // TODO(cushon): remove this after updating javac
+    if (bytes[7] > 53) {
+      bytes[7] = 53;
+    }
+    je.setSize(bytes.length);
+    CRC32 crc = new CRC32();
+    crc.update(bytes);
+    je.setCrc(crc.getValue());
+    jos.putNextEntry(je);
+    jos.write(bytes);
+  }
+
+  private static byte[] toByteArray(InputStream is) throws IOException {
+    byte[] buffer = new byte[8192];
+    ByteArrayOutputStream boas = new ByteArrayOutputStream();
+    while (true) {
+      int r = is.read(buffer);
+      if (r == -1) {
+        break;
+      }
+      boas.write(buffer, 0, r);
+    }
+    return boas.toByteArray();
+  }
+
+  /**
+   * Returns the major version of the host Java runtime (e.g. '8' for JDK 8), using {@link
+   * Runtime#version} if it is available, and otherwise falling back to the {@code
+   * java.class.version} system. property.
+   */
+  static int hostMajorVersion() {
+    try {
+      Method versionMethod = Runtime.class.getMethod("version");
+      Object version = versionMethod.invoke(null);
+      return (int) version.getClass().getMethod("major").invoke(version);
+    } catch (ReflectiveOperationException e) {
+      // Runtime.version() isn't available on JDK 8; continue below
+    }
+    int version = (int) Double.parseDouble(System.getProperty("java.class.version"));
+    if (49 <= version && version <= 52) {
+      return version - (49 - 5);
+    }
+    throw new IllegalStateException(
+        "Unknown Java version: " + System.getProperty("java.specification.version"));
+  }
+}
diff --git a/toolchains/default_java_toolchain.bzl b/toolchains/default_java_toolchain.bzl
new file mode 100644
index 0000000..abe45e6
--- /dev/null
+++ b/toolchains/default_java_toolchain.bzl
@@ -0,0 +1,302 @@
+# Copyright 2021 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Rules for defining default_java_toolchain"""
+
+load("@rules_java//java:defs.bzl", "java_toolchain")
+
+JDK8_JVM_OPTS = [
+    "-Xbootclasspath/p:$(location @remote_java_tools//:javac_jar)",
+]
+
+# JVM options, without patching java.compiler and jdk.compiler modules.
+BASE_JDK9_JVM_OPTS = [
+    # Allow JavaBuilder to access internal javac APIs.
+    "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
+    "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
+    "--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+    "--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
+    "--add-exports=jdk.compiler/com.sun.tools.javac.resources=ALL-UNNAMED",
+    "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+    "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+    "--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+    "--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
+    "--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
+    "--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
+
+    # quiet warnings from com.google.protobuf.UnsafeUtil,
+    # see: https://github.com/google/protobuf/issues/3781
+    # and: https://github.com/bazelbuild/bazel/issues/5599
+    "--add-opens=java.base/java.nio=ALL-UNNAMED",
+    "--add-opens=java.base/java.lang=ALL-UNNAMED",
+]
+
+JDK9_JVM_OPTS = BASE_JDK9_JVM_OPTS + [
+    # override the javac in the JDK.
+    "--patch-module=java.compiler=$(location @remote_java_tools//:java_compiler_jar)",
+    "--patch-module=jdk.compiler=$(location @remote_java_tools//:jdk_compiler_jar)",
+]
+
+DEFAULT_JAVACOPTS = [
+    "-XDskipDuplicateBridges=true",
+    "-XDcompilePolicy=simple",
+    "-g",
+    "-parameters",
+    # https://github.com/bazelbuild/java_tools/issues/51#issuecomment-927940699
+    "-XepOpt:ReturnValueIgnored:ObjectMethods=false",
+]
+
+# java_toolchain parameters without specifying javac, java.compiler,
+# jdk.compiler module, and jvm_opts
+_BASE_TOOLCHAIN_CONFIGURATION = dict(
+    forcibly_disable_header_compilation = False,
+    genclass = ["@remote_java_tools//:GenClass"],
+    header_compiler = ["@remote_java_tools//:TurbineDirect"],
+    header_compiler_direct = ["@remote_java_tools//:TurbineDirect"],
+    ijar = ["//toolchains:ijar"],
+    javabuilder = ["@remote_java_tools//:JavaBuilder"],
+    javac_supports_workers = True,
+    jacocorunner = "@remote_java_tools//:jacoco_coverage_runner_filegroup",
+    jvm_opts = BASE_JDK9_JVM_OPTS,
+    misc = DEFAULT_JAVACOPTS,
+    singlejar = ["//toolchains:singlejar"],
+    # Code to enumerate target JVM boot classpath uses host JVM. Because
+    # java_runtime-s are involved, its implementation is in @bazel_tools.
+    bootclasspath = ["//toolchains:platformclasspath"],
+    source_version = "8",
+    target_version = "8",
+    reduced_classpath_incompatible_processors = [
+        "dagger.hilt.processor.internal.root.RootProcessor",  # see b/21307381
+    ],
+)
+
+JVM8_TOOLCHAIN_CONFIGURATION = dict(
+    tools = ["@remote_java_tools//:javac_jar"],
+    jvm_opts = ["-Xbootclasspath/p:$(location @remote_java_tools//:javac_jar)"],
+    java_runtime = "//toolchains:jdk_8",
+)
+
+DEFAULT_TOOLCHAIN_CONFIGURATION = dict(
+    jvm_opts = [
+        # Compact strings make JavaBuilder slightly slower.
+        "-XX:-CompactStrings",
+    ] + JDK9_JVM_OPTS,
+    turbine_jvm_opts = [
+        # Turbine is not a worker and parallel GC is faster for short-lived programs.
+        "-XX:+UseParallelOldGC",
+    ],
+    tools = [
+        "@remote_java_tools//:java_compiler_jar",
+        "@remote_java_tools//:jdk_compiler_jar",
+    ],
+    java_runtime = "//toolchains:remote_jdk11",
+)
+
+# The 'vanilla' toolchain is an unsupported alternative to the default.
+#
+# It does not provide any of the following features:
+#   * Error Prone
+#   * Strict Java Deps
+#   * Reduced Classpath Optimization
+#
+# It uses the version of internal javac from the `--host_javabase` JDK instead
+# of providing a javac. Internal javac may not be source- or bug-compatible with
+# the javac that is provided with other toolchains.
+#
+# However it does allow using a wider range of `--host_javabase`s, including
+# versions newer than the current JDK.
+VANILLA_TOOLCHAIN_CONFIGURATION = dict(
+    javabuilder = ["@remote_java_tools//:VanillaJavaBuilder"],
+    jvm_opts = [],
+)
+
+# The new toolchain is using all the pre-built tools, including
+# singlejar and ijar, even on remote execution. This toolchain
+# should be used only when host and execution platform are the
+# same, otherwise the binaries will not work on the execution
+# platform.
+PREBUILT_TOOLCHAIN_CONFIGURATION = dict(
+    jvm_opts = [
+        # Compact strings make JavaBuilder slightly slower.
+        "-XX:-CompactStrings",
+    ] + JDK9_JVM_OPTS,
+    turbine_jvm_opts = [
+        # Turbine is not a worker and parallel GC is faster for short-lived programs.
+        "-XX:+UseParallelOldGC",
+    ],
+    tools = [
+        "@remote_java_tools//:java_compiler_jar",
+        "@remote_java_tools//:jdk_compiler_jar",
+    ],
+    ijar = ["//toolchains:ijar_prebuilt_binary"],
+    singlejar = ["//toolchains:prebuilt_singlejar"],
+    java_runtime = "//toolchains:remote_jdk11",
+)
+
+# The new toolchain is using all the tools from sources.
+NONPREBUILT_TOOLCHAIN_CONFIGURATION = dict(
+    jvm_opts = [
+        # Compact strings make JavaBuilder slightly slower.
+        "-XX:-CompactStrings",
+    ] + JDK9_JVM_OPTS,
+    turbine_jvm_opts = [
+        # Turbine is not a worker and parallel GC is faster for short-lived programs.
+        "-XX:+UseParallelOldGC",
+    ],
+    tools = [
+        "@remote_java_tools//:java_compiler_jar",
+        "@remote_java_tools//:jdk_compiler_jar",
+    ],
+    ijar = ["@remote_java_tools//:ijar_cc_binary"],
+    singlejar = ["@remote_java_tools//:singlejar_cc_bin"],
+    java_runtime = "//toolchains:remote_jdk11",
+)
+
+def default_java_toolchain(name, configuration = DEFAULT_TOOLCHAIN_CONFIGURATION, toolchain_definition = True, **kwargs):
+    """Defines a remote java_toolchain with appropriate defaults for Bazel.
+
+    Args:
+        name: The name of the toolchain
+        configuration: Toolchain configuration
+        toolchain_definition: Whether to define toolchain target and its config setting
+        **kwargs: More arguments for the java_toolchain target
+    """
+
+    toolchain_args = dict(_BASE_TOOLCHAIN_CONFIGURATION)
+    toolchain_args.update(configuration)
+    toolchain_args.update(kwargs)
+    java_toolchain(
+        name = name,
+        **toolchain_args
+    )
+    if toolchain_definition:
+        native.config_setting(
+            name = name + "_version_setting",
+            values = {"java_language_version": toolchain_args["source_version"]},
+            visibility = ["//visibility:private"],
+        )
+        native.toolchain(
+            name = name + "_definition",
+            toolchain_type = "@bazel_tools//tools/jdk:toolchain_type",
+            target_settings = [name + "_version_setting"],
+            toolchain = name,
+        )
+
+def java_runtime_files(name, srcs):
+    """Copies the given sources out of the current Java runtime."""
+
+    native.filegroup(
+        name = name,
+        srcs = srcs,
+    )
+    for src in srcs:
+        native.genrule(
+            name = "gen_%s" % src,
+            srcs = ["//toolchains:current_java_runtime"],
+            toolchains = ["//toolchains:current_java_runtime"],
+            cmd = "cp $(JAVABASE)/%s $@" % src,
+            outs = [src],
+        )
+
+def _bootclasspath_impl(ctx):
+    host_javabase = ctx.attr.host_javabase[java_common.JavaRuntimeInfo]
+
+    # explicitly list output files instead of using TreeArtifact to work around
+    # https://github.com/bazelbuild/bazel/issues/6203
+    classes = [
+        "DumpPlatformClassPath.class",
+    ]
+
+    class_outputs = [
+        ctx.actions.declare_file("%s_classes/%s" % (ctx.label.name, clazz))
+        for clazz in classes
+    ]
+
+    args = ctx.actions.args()
+    args.add("-source")
+    args.add("8")
+    args.add("-target")
+    args.add("8")
+    args.add("-Xlint:-options")
+    args.add("-cp")
+    args.add("%s/lib/tools.jar" % host_javabase.java_home)
+    args.add("-d")
+    args.add(class_outputs[0].dirname)
+    args.add(ctx.file.src)
+
+    ctx.actions.run(
+        executable = "%s/bin/javac" % host_javabase.java_home,
+        mnemonic = "JavaToolchainCompileClasses",
+        inputs = [ctx.file.src] + ctx.files.host_javabase,
+        outputs = class_outputs,
+        arguments = [args],
+    )
+
+    bootclasspath = ctx.outputs.output_jar
+
+    inputs = class_outputs + ctx.files.host_javabase
+
+    args = ctx.actions.args()
+    args.add("-XX:+IgnoreUnrecognizedVMOptions")
+    args.add("--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED")
+    args.add("--add-exports=jdk.compiler/com.sun.tools.javac.platform=ALL-UNNAMED")
+    args.add("--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED")
+    args.add_joined(
+        "-cp",
+        [class_outputs[0].dirname, "%s/lib/tools.jar" % host_javabase.java_home],
+        join_with = ctx.configuration.host_path_separator,
+    )
+    args.add("DumpPlatformClassPath")
+    args.add(bootclasspath)
+
+    if ctx.attr.target_javabase:
+        inputs.extend(ctx.files.target_javabase)
+        args.add(ctx.attr.target_javabase[java_common.JavaRuntimeInfo].java_home)
+
+    ctx.actions.run(
+        executable = str(host_javabase.java_executable_exec_path),
+        mnemonic = "JavaToolchainCompileBootClasspath",
+        inputs = inputs,
+        outputs = [bootclasspath],
+        arguments = [args],
+    )
+    return [
+        DefaultInfo(files = depset([bootclasspath])),
+        OutputGroupInfo(jar = [bootclasspath]),
+    ]
+
+_bootclasspath = rule(
+    implementation = _bootclasspath_impl,
+    attrs = {
+        "host_javabase": attr.label(
+            cfg = "host",
+            providers = [java_common.JavaRuntimeInfo],
+        ),
+        "output_jar": attr.output(mandatory = True),
+        "src": attr.label(
+            cfg = "host",
+            allow_single_file = True,
+        ),
+        "target_javabase": attr.label(
+            providers = [java_common.JavaRuntimeInfo],
+        ),
+    },
+)
+
+def bootclasspath(name, **kwargs):
+    _bootclasspath(
+        name = name,
+        output_jar = name + ".jar",
+        **kwargs
+    )
diff --git a/toolchains/fail_rule.bzl b/toolchains/fail_rule.bzl
new file mode 100644
index 0000000..a8a3da2
--- /dev/null
+++ b/toolchains/fail_rule.bzl
@@ -0,0 +1,35 @@
+# Copyright 2020 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""A rule than fails during analysis."""
+
+def _fail_rule_impl(ctx):
+    if ctx.attr.header:
+        fail("%s %s" % (ctx.attr.header, ctx.attr.message))
+    else:
+        fail(ctx.attr.message)
+
+fail_rule = rule(
+    doc = "A rule that fails during analysis.",
+    implementation = _fail_rule_impl,
+    attrs = {
+        "header": attr.string(
+            doc = "Header of the message.",
+        ),
+        "message": attr.string(
+            mandatory = True,
+            doc = "Message to display.",
+        ),
+    },
+)
diff --git a/toolchains/java_toolchain_alias.bzl b/toolchains/java_toolchain_alias.bzl
new file mode 100644
index 0000000..8cf247b
--- /dev/null
+++ b/toolchains/java_toolchain_alias.bzl
@@ -0,0 +1,114 @@
+# Copyright 2019 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Experimental re-implementations of Java toolchain aliases using toolchain resolution."""
+
+def _java_runtime_alias(ctx):
+    """An experimental implementation of java_runtime_alias using toolchain resolution."""
+    toolchain_info = ctx.toolchains["@bazel_tools//tools/jdk:runtime_toolchain_type"]
+    toolchain = toolchain_info.java_runtime
+    return [
+        toolchain_info,
+        toolchain,
+        platform_common.TemplateVariableInfo({
+            "JAVA": str(toolchain.java_executable_exec_path),
+            "JAVABASE": str(toolchain.java_home),
+        }),
+        # See b/65239471 for related discussion of handling toolchain runfiles/data.
+        DefaultInfo(
+            runfiles = ctx.runfiles(transitive_files = toolchain.files),
+            files = toolchain.files,
+        ),
+    ]
+
+java_runtime_alias = rule(
+    implementation = _java_runtime_alias,
+    toolchains = ["@bazel_tools//tools/jdk:runtime_toolchain_type"],
+    incompatible_use_toolchain_transition = True,
+)
+
+def _java_host_runtime_alias(ctx):
+    """An experimental implementation of java_host_runtime_alias using toolchain resolution."""
+    runtime = ctx.attr._runtime
+    java_runtime = runtime[java_common.JavaRuntimeInfo]
+    template_variable_info = runtime[platform_common.TemplateVariableInfo]
+    toolchain_info = platform_common.ToolchainInfo(java_runtime = java_runtime)
+    return [
+        java_runtime,
+        template_variable_info,
+        toolchain_info,
+        runtime[DefaultInfo],
+    ]
+
+java_host_runtime_alias = rule(
+    implementation = _java_host_runtime_alias,
+    attrs = {
+        "_runtime": attr.label(
+            default = Label("//toolchains:current_java_runtime"),
+            providers = [
+                java_common.JavaRuntimeInfo,
+                platform_common.TemplateVariableInfo,
+            ],
+            cfg = "host",
+        ),
+    },
+    provides = [
+        java_common.JavaRuntimeInfo,
+        platform_common.TemplateVariableInfo,
+        platform_common.ToolchainInfo,
+    ],
+)
+
+def _java_runtime_transition_impl(_settings, attr):
+    return {"//command_line_option:java_runtime_version": attr.runtime_version}
+
+_java_runtime_transition = transition(
+    implementation = _java_runtime_transition_impl,
+    inputs = [],
+    outputs = ["//command_line_option:java_runtime_version"],
+)
+
+java_runtime_version_alias = rule(
+    implementation = _java_runtime_alias,
+    toolchains = ["@bazel_tools//tools/jdk:runtime_toolchain_type"],
+    incompatible_use_toolchain_transition = True,
+    attrs = {
+        "runtime_version": attr.string(mandatory = True),
+        "_allowlist_function_transition": attr.label(
+            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
+        ),
+    },
+    cfg = _java_runtime_transition,
+)
+
+def _java_toolchain_alias(ctx):
+    """An experimental implementation of java_toolchain_alias using toolchain resolution."""
+    toolchain_info = ctx.toolchains["@bazel_tools//tools/jdk:toolchain_type"]
+    toolchain = toolchain_info.java
+
+    # buildifier: disable=rule-impl-return
+    return struct(
+        providers = [
+            toolchain_info,
+            toolchain,
+        ],
+        # Use the legacy provider syntax for compatibility with the native rules.
+        java_toolchain = toolchain,
+    )
+
+java_toolchain_alias = rule(
+    implementation = _java_toolchain_alias,
+    toolchains = ["@bazel_tools//tools/jdk:toolchain_type"],
+    incompatible_use_toolchain_transition = True,
+)
diff --git a/toolchains/jdk.BUILD b/toolchains/jdk.BUILD
new file mode 100644
index 0000000..6870771
--- /dev/null
+++ b/toolchains/jdk.BUILD
@@ -0,0 +1,218 @@
+load("@rules_java//java:defs.bzl", "java_import", "java_runtime")
+
+package(default_visibility = ["//visibility:public"])
+
+exports_files(["BUILD.bazel"])
+
+DEPRECATION_MESSAGE = ("Don't depend on targets in the JDK workspace;" +
+                       " use @bazel_tools//tools/jdk:current_java_runtime instead" +
+                       " (see https://github.com/bazelbuild/bazel/issues/5594)")
+
+filegroup(
+    name = "jni_header",
+    srcs = ["include/jni.h"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jni_md_header-darwin",
+    srcs = ["include/darwin/jni_md.h"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jni_md_header-linux",
+    srcs = ["include/linux/jni_md.h"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jni_md_header-freebsd",
+    srcs = ["include/freebsd/jni_md.h"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jni_md_header-openbsd",
+    srcs = ["include/openbsd/jni_md.h"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jni_md_header-windows",
+    srcs = ["include/win32/jni_md.h"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "java",
+    srcs = select({
+        ":windows": ["bin/java.exe"],
+        "//conditions:default": ["bin/java"],
+    }),
+    data = [":jdk"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jar",
+    srcs = select({
+        ":windows": ["bin/jar.exe"],
+        "//conditions:default": ["bin/jar"],
+    }),
+    data = [":jdk"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "javac",
+    srcs = select({
+        ":windows": ["bin/javac.exe"],
+        "//conditions:default": ["bin/javac"],
+    }),
+    data = [":jdk"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "javadoc",
+    srcs = select({
+        ":windows": ["bin/javadoc.exe"],
+        "//conditions:default": ["bin/javadoc"],
+    }),
+    data = [":jdk"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "xjc",
+    srcs = ["bin/xjc"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "wsimport",
+    srcs = ["bin/wsimport"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+BOOTCLASS_JARS = [
+    "rt.jar",
+    "resources.jar",
+    "jsse.jar",
+    "jce.jar",
+    "charsets.jar",
+]
+
+# TODO(cushon): this isn't compatible with JDK 9
+filegroup(
+    name = "bootclasspath",
+    srcs = ["jre/lib/%s" % jar for jar in BOOTCLASS_JARS],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jre-bin",
+    srcs = select({
+        # In some configurations, Java browser plugin is considered harmful and
+        # common antivirus software blocks access to npjp2.dll interfering with Bazel,
+        # so do not include it in JRE on Windows.
+        ":windows": glob(
+            ["jre/bin/**"],
+            allow_empty = True,
+            exclude = ["jre/bin/plugin2/**"],
+        ),
+        "//conditions:default": glob(
+            ["jre/bin/**"],
+            allow_empty = True,
+        ),
+    }),
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jre-lib",
+    srcs = glob(
+        ["jre/lib/**"],
+        allow_empty = True,
+    ),
+)
+
+filegroup(
+    name = "jre",
+    srcs = [":jre-default"],
+)
+
+filegroup(
+    name = "jre-default",
+    srcs = [
+        ":jre-bin",
+        ":jre-lib",
+    ],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jdk-bin",
+    srcs = glob(
+        ["bin/**"],
+        # The JDK on Windows sometimes contains a directory called
+        # "%systemroot%", which is not a valid label.
+        exclude = ["**/*%*/**"],
+    ),
+)
+
+#This folder holds security policies
+filegroup(
+    name = "jdk-conf",
+    srcs = glob(
+        ["conf/**"],
+        allow_empty = True,
+    ),
+)
+
+filegroup(
+    name = "jdk-include",
+    srcs = glob(["include/**"]),
+)
+
+filegroup(
+    name = "jdk-lib",
+    srcs = glob(
+        ["lib/**"],
+        exclude = [
+            "lib/missioncontrol/**",
+            "lib/visualvm/**",
+        ],
+    ),
+)
+
+java_runtime(
+    name = "jdk",
+    srcs = [
+        ":jdk-bin",
+        ":jdk-conf",
+        ":jdk-include",
+        ":jdk-lib",
+        ":jre-default",
+    ],
+)
+
+filegroup(
+    name = "langtools",
+    srcs = ["lib/tools.jar"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+java_import(
+    name = "langtools-neverlink",
+    deprecation = DEPRECATION_MESSAGE,
+    jars = ["lib/tools.jar"],
+    neverlink = 1,
+)
+
+config_setting(
+    name = "windows",
+    values = {"cpu": "x64_windows"},
+    visibility = ["//visibility:private"],
+)
diff --git a/toolchains/local_java_repository.bzl b/toolchains/local_java_repository.bzl
new file mode 100644
index 0000000..2c13e89
--- /dev/null
+++ b/toolchains/local_java_repository.bzl
@@ -0,0 +1,278 @@
+# Copyright 2021 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Rules for importing and registering a local JDK."""
+
+load("@rules_java//java:defs.bzl", "java_runtime")
+load(":default_java_toolchain.bzl", "JVM8_TOOLCHAIN_CONFIGURATION", "default_java_toolchain")
+
+def _detect_java_version(repository_ctx, java_bin):
+    properties_out = repository_ctx.execute([java_bin, "-XshowSettings:properties"]).stderr
+    # This returns an indented list of properties separated with newlines:
+    # "  java.vendor.url.bug = ... \n"
+    # "  java.version = 11.0.8\n"
+    # "  java.version.date = 2020-11-05\"
+
+    strip_properties = [property.strip() for property in properties_out.splitlines()]
+    version_property = [property for property in strip_properties if property.startswith("java.version = ")]
+    if len(version_property) != 1:
+        return None
+
+    version_value = version_property[0][len("java.version = "):]
+    parts = version_value.split(".")
+    major = parts[0]
+    if len(parts) == 1:
+        return major
+    elif major == "1":  # handles versions below 1.8
+        minor = parts[1]
+        return minor
+    return major
+
+def local_java_runtime(name, java_home, version, runtime_name = None, visibility = ["//visibility:public"]):
+    """Defines a java_runtime target together with Java runtime and compile toolchain definitions.
+
+    Java runtime toolchain is constrained by flag --java_runtime_version having
+    value set to either name or version argument.
+
+    Java compile toolchains are created for --java_language_version flags values
+    between 8 and version (inclusive). Java compile toolchains use the same
+    (local) JDK for compilation. This requires a different configuration for JDK8
+    than the newer versions.
+
+    Args:
+      name: name of the target.
+      java_home: Path to the JDK.
+      version: Version of the JDK.
+      runtime_name: name of java_runtime target if it already exists.
+      visibility: Visibility that will be applied to the java runtime target
+    """
+
+    # The repository name in Bzlmod will be "<module canonical name>.<module extension name>.local_jdk"
+    # instead of "local_jdk", therefore we cannot just use it as the config_setting value,
+    # because it won't match the default value of --java_runtime_version (which is "local_jdk").
+    name = name.split(".")[-1]
+
+    if runtime_name == None:
+        runtime_name = name
+        java_runtime(
+            name = runtime_name,
+            java_home = java_home,
+            visibility = visibility,
+        )
+
+    native.config_setting(
+        name = name + "_name_setting",
+        values = {"java_runtime_version": name},
+        visibility = ["//visibility:private"],
+    )
+    native.config_setting(
+        name = name + "_version_setting",
+        values = {"java_runtime_version": version},
+        visibility = ["//visibility:private"],
+    )
+    native.config_setting(
+        name = name + "_name_version_setting",
+        values = {"java_runtime_version": name + "_" + version},
+        visibility = ["//visibility:private"],
+    )
+    native.alias(
+        name = name + "_settings_alias",
+        actual = select({
+            name + "_name_setting": name + "_name_setting",
+            name + "_version_setting": name + "_version_setting",
+            "//conditions:default": name + "_name_version_setting",
+        }),
+        visibility = ["//visibility:private"],
+    )
+    native.toolchain(
+        name = "runtime_toolchain_definition",
+        target_settings = [":%s_settings_alias" % name],
+        toolchain_type = "@bazel_tools//tools/jdk:runtime_toolchain_type",
+        toolchain = runtime_name,
+    )
+
+    if version == "8":
+        default_java_toolchain(
+            name = name + "_toolchain_java8",
+            configuration = JVM8_TOOLCHAIN_CONFIGURATION,
+            source_version = version,
+            target_version = version,
+            java_runtime = runtime_name,
+        )
+    elif type(version) == type("") and version.isdigit() and int(version) > 8:
+        for version in range(8, int(version) + 1):
+            default_java_toolchain(
+                name = name + "_toolchain_java" + str(version),
+                source_version = str(version),
+                target_version = str(version),
+                java_runtime = runtime_name,
+            )
+
+    # else version is not recognized and no compilation toolchains are predefined
+
+def _is_macos(repository_ctx):
+    return repository_ctx.os.name.lower().find("mac os x") != -1
+
+def _is_windows(repository_ctx):
+    return repository_ctx.os.name.lower().find("windows") != -1
+
+def _with_os_extension(repository_ctx, binary):
+    return binary + (".exe" if _is_windows(repository_ctx) else "")
+
+def _determine_java_home(repository_ctx):
+    """Determine the java home path.
+
+    If the `java_home` attribute is specified, then use the given path,
+    otherwise, try to detect the java home path on the system.
+
+    Args:
+      repository_ctx: repository context
+    """
+    java_home = repository_ctx.attr.java_home
+    if java_home:
+        java_home_path = repository_ctx.path(java_home)
+        if not java_home_path.exists:
+            fail('The path indicated by the "java_home" attribute "%s" (absolute: "%s") ' +
+                 "does not exist." % (java_home, str(java_home_path)))
+        return java_home_path
+    if "JAVA_HOME" in repository_ctx.os.environ:
+        return repository_ctx.path(repository_ctx.os.environ["JAVA_HOME"])
+
+    if _is_macos(repository_ctx):
+        # Replicate GetSystemJavabase() in src/main/cpp/blaze_util_darwin.cc
+        result = repository_ctx.execute(["/usr/libexec/java_home", "-v", "1.11+"])
+        if result.return_code == 0:
+            return repository_ctx.path(result.stdout.strip())
+    else:
+        # Calculate java home by locating the javac binary
+        # javac should exists at ${JAVA_HOME}/bin/javac
+        # Replicate GetSystemJavabase() in src/main/cpp/blaze_util_linux.cc
+        # This logic should also work on Windows.
+        javac_path = repository_ctx.which(_with_os_extension(repository_ctx, "javac"))
+        if javac_path:
+            return javac_path.realpath.dirname.dirname
+    return repository_ctx.path("./nosystemjdk")
+
+def _local_java_repository_impl(repository_ctx):
+    """Repository rule local_java_repository implementation.
+
+    Args:
+      repository_ctx: repository context
+    """
+
+    java_home = _determine_java_home(repository_ctx)
+
+    repository_ctx.file(
+        "WORKSPACE",
+        "# DO NOT EDIT: automatically generated WORKSPACE file for local_java_repository\n" +
+        "workspace(name = \"{name}\")\n".format(name = repository_ctx.name),
+    )
+
+    java_bin = java_home.get_child("bin").get_child(_with_os_extension(repository_ctx, "java"))
+
+    if not java_bin.exists:
+        # Java binary does not exist
+        repository_ctx.file(
+            "BUILD.bazel",
+            _NOJDK_BUILD_TPL.format(
+                local_jdk = repository_ctx.name,
+                java_binary = _with_os_extension(repository_ctx, "bin/java"),
+                java_home = java_home,
+            ),
+            False,
+        )
+        return
+
+    # Detect version
+    version = repository_ctx.attr.version if repository_ctx.attr.version != "" else _detect_java_version(repository_ctx, java_bin)
+
+    # Prepare BUILD file using "local_java_runtime" macro
+    build_file = ""
+    if repository_ctx.attr.build_file != None:
+        build_file = repository_ctx.read(repository_ctx.path(repository_ctx.attr.build_file))
+
+    runtime_name = '"jdk"' if repository_ctx.attr.build_file else None
+    local_java_runtime_macro = """
+local_java_runtime(
+    name = "%s",
+    runtime_name = %s,
+    java_home = "%s",
+    version = "%s",
+)
+""" % (repository_ctx.name, runtime_name, java_home, version)
+
+    repository_ctx.file(
+        "BUILD.bazel",
+        'load("@rules_java//toolchains:local_java_repository.bzl", "local_java_runtime")\n' +
+        build_file +
+        local_java_runtime_macro,
+    )
+
+    # Symlink all files
+    for file in java_home.readdir():
+        repository_ctx.symlink(file, file.basename)
+
+# Build file template, when JDK does not exist
+_NOJDK_BUILD_TPL = '''load("@rules_java//toolchains:fail_rule.bzl", "fail_rule")
+fail_rule(
+   name = "jdk",
+   header = "Auto-Configuration Error:",
+   message = ("Cannot find Java binary {java_binary} in {java_home}; either correct your JAVA_HOME, " +
+          "PATH or specify Java from remote repository (e.g. " +
+          "--java_runtime_version=remotejdk_11")
+)
+config_setting(
+   name = "localjdk_setting",
+   values = {{"java_runtime_version": "{local_jdk}"}},
+   visibility = ["//visibility:private"],
+)
+toolchain(
+   name = "runtime_toolchain_definition",
+   target_settings = [":localjdk_setting"],
+   toolchain_type = "@bazel_tools//tools/jdk:runtime_toolchain_type",
+   toolchain = ":jdk",
+)
+'''
+
+_local_java_repository_rule = repository_rule(
+    implementation = _local_java_repository_impl,
+    local = True,
+    configure = True,
+    environ = ["JAVA_HOME"],
+    attrs = {
+        "build_file": attr.label(),
+        "java_home": attr.string(),
+        "version": attr.string(),
+    },
+)
+
+def local_java_repository(name, java_home = "", version = "", build_file = None):
+    """Registers a runtime toolchain for local JDK and creates an unregistered compile toolchain.
+
+    Toolchain resolution is constrained with --java_runtime_version flag
+    having value of the "name" or "version" parameter.
+
+    Java compile toolchains are created for --java_language_version flags values
+    between 8 and version (inclusive). Java compile toolchains use the same
+    (local) JDK for compilation.
+
+    If there is no JDK "virtual" targets are created, which fail only when actually needed.
+
+    Args:
+      name: A unique name for this rule.
+      java_home: Location of the JDK imported.
+      build_file: optionally BUILD file template
+      version: optionally java version
+    """
+    _local_java_repository_rule(name = name, java_home = java_home, version = version, build_file = build_file)
diff --git a/toolchains/remote_java_repository.bzl b/toolchains/remote_java_repository.bzl
new file mode 100644
index 0000000..31b93b8
--- /dev/null
+++ b/toolchains/remote_java_repository.bzl
@@ -0,0 +1,87 @@
+# Copyright 2020 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Rules for importing and registering JDKs from http archive.
+
+Rule remote_java_repository imports and registers JDK with the toolchain resolution.
+"""
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+def _toolchain_config_impl(ctx):
+    ctx.file("WORKSPACE", "workspace(name = \"{name}\")\n".format(name = ctx.name))
+    ctx.file("BUILD.bazel", ctx.attr.build_file)
+
+_toolchain_config = repository_rule(
+    local = True,
+    implementation = _toolchain_config_impl,
+    attrs = {
+        "build_file": attr.string(),
+    },
+)
+
+def remote_java_repository(name, version, target_compatible_with = None, prefix = "remotejdk", **kwargs):
+    """Imports and registers a JDK from a http archive.
+
+    Toolchain resolution is determined with target_compatible_with
+    parameter and constrained with --java_runtime_version flag either having value
+    of "version" or "{prefix}_{version}" parameters.
+
+    Args:
+      name: A unique name for this rule.
+      version: Version of the JDK imported.
+      target_compatible_with: Target platform constraints (CPU and OS) for this JDK.
+      prefix: Optional alternative prefix for configuration flag value used to determine this JDK.
+      **kwargs: Refer to http_archive documentation
+    """
+    http_archive(
+        name = name,
+        build_file = Label("//toolchains:jdk.BUILD"),
+        **kwargs
+    )
+    _toolchain_config(
+        name = name + "_toolchain_config_repo",
+        build_file = """
+config_setting(
+    name = "prefix_version_setting",
+    values = {{"java_runtime_version": "{prefix}_{version}"}},
+    visibility = ["//visibility:private"],
+)
+config_setting(
+    name = "version_setting",
+    values = {{"java_runtime_version": "{version}"}},
+    visibility = ["//visibility:private"],
+)
+alias(
+    name = "version_or_prefix_version_setting",
+    actual = select({{
+        ":version_setting": ":version_setting",
+        "//conditions:default": ":prefix_version_setting",
+    }}),
+    visibility = ["//visibility:private"],
+)
+toolchain(
+    name = "toolchain",
+    target_compatible_with = {target_compatible_with},
+    target_settings = [":version_or_prefix_version_setting"],
+    toolchain_type = "@bazel_tools//tools/jdk:runtime_toolchain_type",
+    toolchain = "{toolchain}",
+)
+""".format(
+            prefix = prefix,
+            version = version,
+            target_compatible_with = target_compatible_with,
+            toolchain = "@{repo}//:jdk".format(repo = name),
+        ),
+    )