You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by mg...@apache.org on 2026/03/09 15:33:03 UTC
(kudu) 01/02: [build][java] KUDU-3681: Move to JDK17
This is an automated email from the ASF dual-hosted git repository.
mgreber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git
commit 9b8aef91451cbbf646a59232eec8f3acbb7016a4
Author: zchovan <zc...@cloudera.com>
AuthorDate: Mon Aug 4 15:24:11 2025 +0200
[build][java] KUDU-3681: Move to JDK17
This change aims to support building and running the java bits with Java 17.
The changes:
* natively set release version when building with Java 9+
* use 'org.apache.tomcat:annotations-api' as a compileOnly dependency for the
'javax.annotations' package, since that was deprecated in Java 9
* upgrade jacoco dependency from 0.8.6 to 0.8.8 for Java17 compatibility
* upgrade the Hive dependency to 4.1.0
* after this change, it is no longer possible to compile Kudu java bits
with JDK versions lower than 17
Change-Id: I1f73c0d3a79e1c37b12a173881273b4b68d718b3
Reviewed-on: http://gerrit.cloudera.org:8080/23259
Reviewed-by: Marton Greber <gr...@gmail.com>
Tested-by: Marton Greber <gr...@gmail.com>
Reviewed-by: Gabriella Lotz <lo...@gmail.com>
---
java/config/spotbugs/excludeFilter.xml | 2 ++
java/gradle.properties | 11 ---------
java/gradle/compile.gradle | 9 ++++----
java/gradle/dependencies.gradle | 11 +++++----
java/gradle/docs.gradle | 2 +-
java/gradle/quality.gradle | 27 ++++++++++++++--------
java/gradle/tests.gradle | 8 +++++--
java/kudu-backup-common/build.gradle | 2 ++
java/kudu-hive/build.gradle | 1 +
.../hive/metastore/TestKuduMetastorePlugin.java | 24 +++++++++----------
java/kudu-proto/build.gradle | 1 +
.../apache/kudu/spark/kudu/DefaultSourceTest.scala | 8 +++----
src/kudu/ranger-kms/mini_ranger_kms.cc | 16 +++++++++++++
src/kudu/ranger/mini_ranger.cc | 12 ++++++++++
14 files changed, 86 insertions(+), 48 deletions(-)
diff --git a/java/config/spotbugs/excludeFilter.xml b/java/config/spotbugs/excludeFilter.xml
index 005fbea26..a33205b82 100644
--- a/java/config/spotbugs/excludeFilter.xml
+++ b/java/config/spotbugs/excludeFilter.xml
@@ -130,6 +130,8 @@
<Bug pattern="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT" />
<!-- Tests often cast to a known class. -->
<Bug pattern="BC_UNCONFIRMED_CAST_OF_RETURN_VALUE" />
+ <!-- In tests, instanceof assertions are often intentional for readability. -->
+ <Bug pattern="JUA_DONT_ASSERT_INSTANCEOF_IN_TESTS" />
<!-- Tests don't need to worry about serialization compatibility. -->
<Bug pattern="SE_NO_SERIALVERSIONID" />
<!-- Often tests testing exceptions have "exception" in the name. -->
diff --git a/java/gradle.properties b/java/gradle.properties
index b490f9fb5..f9271dddf 100755
--- a/java/gradle.properties
+++ b/java/gradle.properties
@@ -25,17 +25,6 @@ url = https://kudu.apache.org/
# The Maven respository used when uploading artifacts.
mavenPublishUrl = https://repository.apache.org/service/local/staging/deploy/maven2
-# Version passed to javac's -source, -target, and --release (Java 9+) parameters:
-#
-# -source Specifies the version of source code accepted.
-# -target Generate class files that target a specified version of the VM. Class
-# files will run on the specified target and on later versions, but not
-# on earlier versions of the VM.
-# --release (Java 9+) Configures the compiler to produce class files that will
-# link against an implementation of the given platform version.
-# (See http://openjdk.java.net/jeps/247)
-javaCompatibility = 8
-
encoding = UTF-8
# Used by the scalafmt plugin because the encoding property can't be passed.
systemProp.file.encoding = UTF-8
diff --git a/java/gradle/compile.gradle b/java/gradle/compile.gradle
index 896b24a40..5ea7d36fe 100644
--- a/java/gradle/compile.gradle
+++ b/java/gradle/compile.gradle
@@ -19,15 +19,14 @@
// JVM based compiler configuration
tasks.withType(AbstractCompile) {
- sourceCompatibility = "1.$javaCompatibility"
- targetCompatibility = "1.$javaCompatibility"
// --release is the recommended way to select the target release, but it's only supported in
// Java 9+ so we also set -source and -target via `sourceCompatibility` and `targetCompatibility`.
- // If/when Gradle supports `--release` natively (https://github.com/gradle/gradle/issues/2510),
- // we should switch to that.
+ // The release option takes precedence over source/targetCompatibility:
+ // https://docs.gradle.org/7.6.4/dsl/org.gradle.api.tasks.compile.CompileOptions.html#org.gradle.api.tasks.compile.CompileOptions:release
if (JavaVersion.current().isJava9Compatible()) {
- options.compilerArgs << "--release" << "$javaCompatibility"
+ options.release = 17
}
+
options.encoding = encoding // make sure the encoding is defined by the project and not the system default.
options.incremental = true // enable incremental compilation.
options.compilerArgs << '-proc:none' // Ignore leaked annotation processors on the compile classpath.
diff --git a/java/gradle/dependencies.gradle b/java/gradle/dependencies.gradle
index baa15239f..2118e2f6f 100755
--- a/java/gradle/dependencies.gradle
+++ b/java/gradle/dependencies.gradle
@@ -25,6 +25,7 @@ ext {
}
versions += [
+ annotations : "6.0.53",
assertj : "3.27.3",
async : "1.4.1",
checkstyle : "8.36.1",
@@ -41,9 +42,9 @@ versions += [
hadoop : "3.4.1",
hamcrest : "3.0",
hdrhistogram : "2.2.2",
- hive : "3.1.2",
+ hive : "4.1.0",
httpClient : "4.5.14",
- jacoco : "0.8.6",
+ jacoco : "0.8.8",
jepsen : "0.1.5",
jetty : "9.4.57.v20241219",
jmh : "1.37",
@@ -65,7 +66,7 @@ versions += [
slf4j : "1.7.36",
spark2 : "2.4.8",
spark : "3.5.5",
- spotBugs : "4.1.1",
+ spotBugs : "4.2.2",
yetus : "0.13.0"
]
@@ -89,6 +90,7 @@ versions["sparkBase"] = versions.spark.substring(0, versions.spark.indexOf("."))
libs += [
assertj : "org.assertj:assertj-core:$versions.assertj",
+ annotations : "org.apache.tomcat:annotations-api:$versions.annotations",
async : "com.stumbleupon:async:$versions.async",
clojure : "org.clojure:clojure:$versions.clojure",
clojureToolsCli : "org.clojure:tools.cli:$versions.clojureToolsCli",
@@ -108,12 +110,13 @@ libs += [
guava : "com.google.guava:guava:$versions.guava",
hadoopClient : "org.apache.hadoop:hadoop-client:$versions.hadoop",
hadoopCommon : "org.apache.hadoop:hadoop-common:$versions.hadoop",
+ hadoopHdfs : "org.apache.hadoop:hadoop-hdfs:$versions.hadoop",
hadoopMiniCluster : "org.apache.hadoop:hadoop-minicluster:$versions.hadoop",
hadoopMRClientCommon : "org.apache.hadoop:hadoop-mapreduce-client-common:$versions.hadoop",
hadoopMRClientCore : "org.apache.hadoop:hadoop-mapreduce-client-core:$versions.hadoop",
hamcrest : "org.hamcrest:hamcrest:$versions.hamcrest",
hdrhistogram : "org.hdrhistogram:HdrHistogram:$versions.hdrhistogram",
- hiveMetastore : "org.apache.hive:hive-metastore:$versions.hive",
+ hiveMetastore : "org.apache.hive:hive-standalone-metastore-server:$versions.hive",
hiveTestUtils : "org.apache.hive:hive-testutils:$versions.hive",
httpClient : "org.apache.httpcomponents:httpclient:$versions.httpClient",
httpMime : "org.apache.httpcomponents:httpmime:$versions.httpClient",
diff --git a/java/gradle/docs.gradle b/java/gradle/docs.gradle
index d9ef7cf08..851b724a4 100644
--- a/java/gradle/docs.gradle
+++ b/java/gradle/docs.gradle
@@ -38,7 +38,7 @@ afterEvaluate {
options.docletpath = configurations.doclet.files.asType(List)
options.doclet = "org.apache.yetus.audience.tools.IncludePublicAnnotationsStandardDoclet"
// Link to Java SE javadoc
- options.links "https://docs.oracle.com/javase/$javaCompatibility/docs/api/"
+ options.links "https://docs.oracle.com/javase/17/docs/api/"
// Hacky workaround to YETUS-627. Replace with options.noTimestamp
// once this workaround is no longer needed.
options.addStringOption("notimestamp", "org.apache.kudu")
diff --git a/java/gradle/quality.gradle b/java/gradle/quality.gradle
index ffc10f3b9..7b0391370 100644
--- a/java/gradle/quality.gradle
+++ b/java/gradle/quality.gradle
@@ -135,16 +135,25 @@ if (project.name != "kudu-proto" && project.name != "kudu-jepsen" &&
}
}
-// Define a Java API signature for use by animal-sniffer. It'll enforce that all
-// Java API usage adheres to this signature.
-dependencies {
- signature "org.codehaus.mojo.signature:java1$javaCompatibility:1.0@signature"
-}
+def enableAnimalSniffer = !JavaVersion.current().isJava11Compatible()
+if (enableAnimalSniffer) {
+ // Define a Java API signature for use by animal-sniffer. It'll enforce that all
+ // Java API usage adheres to this signature.
+ dependencies {
+ signature "org.codehaus.mojo.signature:java18:1.0@signature"
+ }
-// Create an aggregate animal-sniffer task.
-// This simplifies running animal-sniffer on all the code by only needing one task instead of multiple in your command.
-task animalsniffer(dependsOn: [animalsnifferMain, animalsnifferTest], group: "Verification") {
- description = "Run animal-sniffer analysis."
+ // Create an aggregate animal-sniffer task.
+ // This simplifies running animal-sniffer on all the code by only needing one task instead of multiple in your command.
+ task animalsniffer(dependsOn: [animalsnifferMain, animalsnifferTest], group: "Verification") {
+ description = "Run animal-sniffer analysis."
+ }
+} else {
+ // Animal Scents signatures are only available up to Java 8.
+ // On JDK 11+, animalsniffer reports false positives for valid JDK APIs.
+ tasks.matching { task -> task.name.startsWith("animalsniffer") }.configureEach {
+ enabled = false
+ }
}
// Configure the versions plugin to only show dependency updates for released versions.
diff --git a/java/gradle/tests.gradle b/java/gradle/tests.gradle
index 4157c559b..0946c97b3 100644
--- a/java/gradle/tests.gradle
+++ b/java/gradle/tests.gradle
@@ -46,16 +46,20 @@ tasks.withType(Test) {
jvmArgs += "-Xmx1g"
// Hide the Java 9+ warnings about illegal reflective access
if (JavaVersion.current().isJava9Compatible()) {
- jvmArgs += "--illegal-access=permit"
def reflectionModules = [
"java.base/java.io", // java.io reflection is used by Spark
"java.base/java.lang", // java.lang reflection is used by TestUtils
+ "java.base/java.lang.reflect", // java.io reflection is used by Spark
"java.base/java.net", // java.net reflection is used by FakeDNS
"java.base/java.nio", // java.nio reflection is used by Protobuf
"java.base/java.util", // java.util reflection is used by Spark
"java.base/java.util.concurrent", // java.util.concurrent reflection is used by Spark
+ "java.base/java.util.concurrent.atomic", // java.io reflection is used by Spark
"java.base/sun.nio.ch", // sun.nio.ch reflection is used by Netty
- "java.security.jgss/sun.security.krb5" // sun.security.krb5 reflection is used by Hadoop's KerberosUtil
+ "java.base/sun.nio.cs", // java.io reflection is used by Spark
+ "java.base/sun.security.action", // java.io reflection is used by Spark
+ "java.base/sun.util.calendar", // java.io reflection is used by Spark
+ "java.security.jgss/sun.security.krb5", // sun.security.krb5 reflection is used by Hadoop's KerberosUtil
]
reflectionModules.forEach { module ->
jvmArgs += "--add-opens=$module=ALL-UNNAMED"
diff --git a/java/kudu-backup-common/build.gradle b/java/kudu-backup-common/build.gradle
index 6a6292315..1fd36ef4d 100644
--- a/java/kudu-backup-common/build.gradle
+++ b/java/kudu-backup-common/build.gradle
@@ -32,6 +32,8 @@ dependencies {
implementation libs.yetusAnnotations
+ compileOnly libs.annotations
+
testImplementation project(path: ":kudu-test-utils", configuration: "shadow")
testImplementation libs.junit
testImplementation libs.log4jApi
diff --git a/java/kudu-hive/build.gradle b/java/kudu-hive/build.gradle
index d86fe8841..462a80e70 100644
--- a/java/kudu-hive/build.gradle
+++ b/java/kudu-hive/build.gradle
@@ -37,6 +37,7 @@ dependencies {
testImplementation libs.hiveMetastore
testImplementation libs.hadoopCommon
+ testImplementation libs.hadoopHdfs
testImplementation libs.hadoopMRClientCommon
}
diff --git a/java/kudu-hive/src/test/java/org/apache/kudu/hive/metastore/TestKuduMetastorePlugin.java b/java/kudu-hive/src/test/java/org/apache/kudu/hive/metastore/TestKuduMetastorePlugin.java
index 589637adb..63d407b41 100644
--- a/java/kudu-hive/src/test/java/org/apache/kudu/hive/metastore/TestKuduMetastorePlugin.java
+++ b/java/kudu-hive/src/test/java/org/apache/kudu/hive/metastore/TestKuduMetastorePlugin.java
@@ -28,7 +28,6 @@ import java.util.UUID;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.metastore.DefaultPartitionExpressionProxy;
import org.apache.hadoop.hive.metastore.HiveMetaStoreClient;
import org.apache.hadoop.hive.metastore.MetaStoreEventListener;
@@ -41,7 +40,7 @@ import org.apache.hadoop.hive.metastore.api.StorageDescriptor;
import org.apache.hadoop.hive.metastore.api.Table;
import org.apache.hadoop.hive.metastore.api.hive_metastoreConstants;
import org.apache.hadoop.hive.metastore.conf.MetastoreConf;
-import org.apache.hadoop.hive.metastore.utils.MetaStoreUtils;
+import org.apache.hadoop.hive.metastore.utils.MetaStoreServerUtils;
import org.apache.thrift.TException;
import org.junit.After;
import org.junit.Test;
@@ -53,7 +52,7 @@ import org.apache.kudu.test.cluster.MiniKuduCluster;
public class TestKuduMetastorePlugin {
private static final Logger LOG = LoggerFactory.getLogger(TestKuduMetastorePlugin.class);
- private HiveConf clientConf;
+ private Configuration clientConf;
private HiveMetaStoreClient client;
private MiniKuduCluster miniCluster;
@@ -108,10 +107,11 @@ public class TestKuduMetastorePlugin {
assertTrue(derbyLogFile.toFile().createNewFile());
System.setProperty("derby.stream.error.file", derbyLogFile.toString());
- int msPort = MetaStoreUtils.startMetaStore(hmsConf);
+ int msPort = MetaStoreServerUtils.startMetaStore(hmsConf);
- clientConf = new HiveConf();
- clientConf.setVar(HiveConf.ConfVars.METASTOREURIS, "thrift://localhost:" + msPort);
+ clientConf = MetastoreConf.newMetastoreConf(hmsConf);
+ clientConf.set(MetastoreConf.ConfVars.THRIFT_URIS.getVarname(),
+ "thrift://localhost:" + msPort);
client = new HiveMetaStoreClient(clientConf);
@@ -327,7 +327,7 @@ public class TestKuduMetastorePlugin {
// Also change the location to avoid MetastoreDefaultTransformer validation
// that exists in some Hive versions.
alteredTable.getSd().setLocation(String.format("%s/%s/%s",
- clientConf.get(HiveConf.ConfVars.METASTOREWAREHOUSE.varname),
+ clientConf.get(MetastoreConf.ConfVars.WAREHOUSE.getVarname()),
table.getDbName(), table.getTableName()));
alteredTable.putToParameters(KuduMetastorePlugin.EXTERNAL_TABLE_KEY, "FALSE");
alteredTable.putToParameters(KuduMetastorePlugin.EXTERNAL_PURGE_KEY, "FALSE");
@@ -341,7 +341,7 @@ public class TestKuduMetastorePlugin {
// Also change the location to avoid MetastoreDefaultTransformer validation
// that exists in some Hive versions.
alteredTable.getSd().setLocation(String.format("%s/%s/%s",
- clientConf.get(HiveConf.ConfVars.METASTOREWAREHOUSE.varname),
+ clientConf.get(MetastoreConf.ConfVars.WAREHOUSE.getVarname()),
table.getDbName(), table.getTableName()));
alteredTable.putToParameters(KuduMetastorePlugin.EXTERNAL_PURGE_KEY, "FALSE");
client.alter_table(table.getDbName(), table.getTableName(), alteredTable);
@@ -357,7 +357,7 @@ public class TestKuduMetastorePlugin {
// Also change the location to avoid MetastoreDefaultTransformer validation
// that exists in some Hive versions.
alteredTable.getSd().setLocation(String.format("%s/%s/%s",
- clientConf.get(HiveConf.ConfVars.METASTOREWAREHOUSE.varname),
+ clientConf.get(MetastoreConf.ConfVars.WAREHOUSE.getVarname()),
table.getDbName(), table.getTableName()));
alteredTable.setTableType(TableType.EXTERNAL_TABLE.toString());
alteredTable.putToParameters(KuduMetastorePlugin.EXTERNAL_TABLE_KEY, "TRUE");
@@ -376,7 +376,7 @@ public class TestKuduMetastorePlugin {
// Also change the location to avoid MetastoreDefaultTransformer validation
// that exists in some Hive versions.
alteredTable.getSd().setLocation(String.format("%s/%s/%s",
- clientConf.get(HiveConf.ConfVars.METASTOREWAREHOUSE.varname),
+ clientConf.get(MetastoreConf.ConfVars.WAREHOUSE.getVarname()),
table.getDbName(), table.getTableName()));
client.alter_table(table.getDbName(), table.getTableName(), alteredTable);
}
@@ -391,7 +391,7 @@ public class TestKuduMetastorePlugin {
// Also change the location to avoid MetastoreDefaultTransformer validation
// that exists in some Hive versions.
alteredTable.getSd().setLocation(String.format("%s/%s/%s",
- clientConf.get(HiveConf.ConfVars.METASTOREWAREHOUSE.varname),
+ clientConf.get(MetastoreConf.ConfVars.WAREHOUSE.getVarname()),
table.getDbName(), table.getTableName()));
client.alter_table(table.getDbName(), table.getTableName(), alteredTable);
}
@@ -401,7 +401,7 @@ public class TestKuduMetastorePlugin {
// Also change the location to avoid MetastoreDefaultTransformer validation
// that exists in some Hive versions.
table.getSd().setLocation(String.format("%s/%s/%s",
- clientConf.get(HiveConf.ConfVars.METASTOREWAREHOUSE.varname),
+ clientConf.get(MetastoreConf.ConfVars.WAREHOUSE.getVarname()),
table.getDbName(), table.getTableName()));
try {
client.alter_table(table.getDbName(), table.getTableName(), table);
diff --git a/java/kudu-proto/build.gradle b/java/kudu-proto/build.gradle
index 98ee30346..2929eb37d 100644
--- a/java/kudu-proto/build.gradle
+++ b/java/kudu-proto/build.gradle
@@ -19,6 +19,7 @@ apply from: "$rootDir/gradle/protobuf.gradle"
dependencies {
implementation libs.protobufJava
+ compileOnly libs.annotations
}
// Add protobuf files to the proto source set.
diff --git a/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/DefaultSourceTest.scala b/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/DefaultSourceTest.scala
index 4dea01444..28a7ad43a 100644
--- a/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/DefaultSourceTest.scala
+++ b/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/DefaultSourceTest.scala
@@ -31,6 +31,7 @@ import org.apache.spark.sql.types.StructField
import org.apache.spark.sql.types.StructType
import org.junit.Assert._
import org.apache.kudu.client.CreateTableOptions
+import org.apache.kudu.client.KuduException
import org.apache.kudu.test.KuduTestHarness
import org.apache.kudu.test.RandomUtils
import org.apache.kudu.spark.kudu.SparkListenerUtil.withJobTaskCounter
@@ -966,7 +967,7 @@ class DefaultSourceTest extends KuduTestSuite with Matchers {
val df = sqlContext.read.options(kuduOptions).format("kudu").load
df.count()
}
- assertTrue(exception.getCause.getMessage.contains("this client is not authenticated"))
+ assertTrue(exception.getMessage.contains("this client is not authenticated"))
KuduClientCache.clearCacheForTests()
kuduOptions = Map(
@@ -992,7 +993,7 @@ class DefaultSourceTest extends KuduTestSuite with Matchers {
df.count
}
assertTrue(
- exception.getCause.getMessage
+ exception.getMessage
.contains("client requires authentication, but server does not have Kerberos enabled"))
}
@@ -1010,8 +1011,7 @@ class DefaultSourceTest extends KuduTestSuite with Matchers {
val df = sqlContext.read.options(kuduOptions).format("kudu").load
df.count
}
- assertTrue(
- exception.getCause.getMessage.contains("server does not support required TLS encryption"))
+ assertTrue(exception.getMessage.contains("server does not support required TLS encryption"))
}
@Test
diff --git a/src/kudu/ranger-kms/mini_ranger_kms.cc b/src/kudu/ranger-kms/mini_ranger_kms.cc
index 5952a91eb..92ac6efe9 100644
--- a/src/kudu/ranger-kms/mini_ranger_kms.cc
+++ b/src/kudu/ranger-kms/mini_ranger_kms.cc
@@ -243,6 +243,22 @@ Status MiniRangerKMS::StartRangerKMS() {
std::vector<string> args({
JoinPathSegments(java_home_, "bin/java"),
"-Djava.net.preferIPv4Stack=true", // ensure IPv4 is used
+ // Ranger KMS and its dependencies use deep reflection that is restricted
+ // by the Java module system in JDK 9+ (including JDK 17).
+ // Keep Java 8 compatibility by ignoring unknown options there.
+ "-XX:+IgnoreUnrecognizedVMOptions",
+ "--add-opens=java.base/java.lang=ALL-UNNAMED",
+ "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED",
+ "--add-opens=java.base/java.io=ALL-UNNAMED",
+ "--add-opens=java.base/java.net=ALL-UNNAMED",
+ "--add-opens=java.base/java.util=ALL-UNNAMED",
+ "--add-opens=java.base/java.util.concurrent=ALL-UNNAMED",
+ "--add-opens=java.base/sun.security.action=ALL-UNNAMED",
+ "--add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED",
+ "--add-opens=java.base/com.sun.crypto.provider=ALL-UNNAMED",
+ // RangerMasterKey directly uses com.sun.org.apache.xml.internal.security.utils.Base64
+ // from the java.xml.crypto module to encode/decode the encrypted master key.
+ "--add-exports=java.xml.crypto/com.sun.org.apache.xml.internal.security.utils=ALL-UNNAMED",
"-Dproc_rangerkms",
Substitute("-Dhostname=$0", host_),
Substitute("-Dlog4j.configuration=file:$0",
diff --git a/src/kudu/ranger/mini_ranger.cc b/src/kudu/ranger/mini_ranger.cc
index de577bac7..d32b0a1a4 100644
--- a/src/kudu/ranger/mini_ranger.cc
+++ b/src/kudu/ranger/mini_ranger.cc
@@ -194,6 +194,18 @@ Status MiniRanger::StartRanger() {
std::vector<string> args({
JoinPathSegments(java_home_, "bin/java"),
"-Djava.net.preferIPv4Stack=true", // ensure IPv4 is used
+ // Ranger and its dependencies use deep reflection that is restricted by
+ // the Java module system in JDK 9+ (including JDK 17).
+ // Keep Java 8 compatibility by ignoring unknown options there.
+ "-XX:+IgnoreUnrecognizedVMOptions",
+ "--add-opens=java.base/java.lang=ALL-UNNAMED",
+ "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED",
+ "--add-opens=java.base/java.io=ALL-UNNAMED",
+ "--add-opens=java.base/java.net=ALL-UNNAMED",
+ "--add-opens=java.base/java.util=ALL-UNNAMED",
+ "--add-opens=java.base/java.util.concurrent=ALL-UNNAMED",
+ "--add-opens=java.base/sun.security.action=ALL-UNNAMED",
+ "--add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED",
"-Dproc_rangeradmin",
Substitute("-Dhostname=$0", host_),
Substitute("-Dlog4j.configuration=file:$0",