Skip to content

Commit 0118330

Browse files
authored
KAFKA-13273: Add support for Java 17 (apache#11296)
Java 17 is at release candidate stage and it will be a LTS release once it's out (previous LTS release was Java 11). Details: * Replace Java 16 with Java 17 in Jenkins and Readme. * Replace `--illegal-access=permit` (which was removed from Java 17) with `--add-opens` for the packages we require internal access to. Filed KAFKA-13275 for updating the tests not to require `--add-opens` (where possible). * Update `release.py` to use JDK8. and JDK 17 (instead of JDK 8 and JDK 15). * Removed all but one Streams test from `testsToExclude`. The Connect test exclusion list remains the same. * Add notable change to upgrade.html * Upgrade to Gradle 7.2 as it's required for proper Java 17 support. * Upgrade mockito to 3.12.4 for better Java 17 support. * Adjusted `KafkaRaftClientTest` and `QuorumStateTest` not to require private access to `jdk.internal.util.random`. Reviewers: Manikumar Reddy <[email protected]>, Chia-Ping Tsai <[email protected]>
1 parent 81667e2 commit 0118330

12 files changed

+125
-79
lines changed

Jenkinsfile

+6-6
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,10 @@ pipeline {
142142
}
143143
}
144144

145-
stage('JDK 16 and Scala 2.13') {
145+
stage('JDK 17 and Scala 2.13') {
146146
agent { label 'ubuntu' }
147147
tools {
148-
jdk 'jdk_16_latest'
148+
jdk 'jdk_17_latest'
149149
}
150150
options {
151151
timeout(time: 8, unit: 'HOURS')
@@ -157,7 +157,7 @@ pipeline {
157157
steps {
158158
doValidation()
159159
doTest(env)
160-
echo 'Skipping Kafka Streams archetype test for Java 16'
160+
echo 'Skipping Kafka Streams archetype test for Java 17'
161161
}
162162
}
163163

@@ -231,14 +231,14 @@ pipeline {
231231
}
232232
}
233233

234-
stage('JDK 16 and Scala 2.12') {
234+
stage('JDK 17 and Scala 2.12') {
235235
when {
236236
not { changeRequest() }
237237
beforeAgent true
238238
}
239239
agent { label 'ubuntu' }
240240
tools {
241-
jdk 'jdk_16_latest'
241+
jdk 'jdk_17_latest'
242242
}
243243
options {
244244
timeout(time: 8, unit: 'HOURS')
@@ -250,7 +250,7 @@ pipeline {
250250
steps {
251251
doValidation()
252252
doTest(env)
253-
echo 'Skipping Kafka Streams archetype test for Java 16'
253+
echo 'Skipping Kafka Streams archetype test for Java 17'
254254
}
255255
}
256256
}

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ See our [web site](https://kafka.apache.org) for details on the project.
44

55
You need to have [Java](http://www.oracle.com/technetwork/java/javase/downloads/index.html) installed.
66

7-
We build and test Apache Kafka with Java 8, 11 and 16. We set the `release` parameter in javac and scalac
7+
We build and test Apache Kafka with Java 8, 11 and 17. We set the `release` parameter in javac and scalac
88
to `8` to ensure the generated binaries are compatible with Java 8 or higher (independently of the Java version
99
used for compilation). Java 8 support has been deprecated since Apache Kafka 3.0 and will be removed in Apache
1010
Kafka 4.0 (see [KIP-750](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=181308223) for more details).

build.gradle

+18-5
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,22 @@ ext {
102102

103103
defaultMaxHeapSize = "2g"
104104
defaultJvmArgs = ["-Xss4m", "-XX:+UseParallelGC"]
105-
if (JavaVersion.current() == JavaVersion.VERSION_16)
106-
defaultJvmArgs.add("--illegal-access=permit")
105+
106+
// "JEP 403: Strongly Encapsulate JDK Internals" causes some tests to fail when they try
107+
// to access internals (often via mocking libraries). We use `--add-opens` as a workaround
108+
// for now and we'll fix it properly (where possible) via KAFKA-13275.
109+
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_16))
110+
defaultJvmArgs.addAll(
111+
"--add-opens=java.base/java.io=ALL-UNNAMED",
112+
"--add-opens=java.base/java.nio=ALL-UNNAMED",
113+
"--add-opens=java.base/java.nio.file=ALL-UNNAMED",
114+
"--add-opens=java.base/java.util.concurrent=ALL-UNNAMED",
115+
"--add-opens=java.base/java.util.regex=ALL-UNNAMED",
116+
"--add-opens=java.base/java.util.stream=ALL-UNNAMED",
117+
"--add-opens=java.base/java.text=ALL-UNNAMED",
118+
"--add-opens=java.base/java.time=ALL-UNNAMED",
119+
"--add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED"
120+
)
107121

108122
userMaxForks = project.hasProperty('maxParallelForks') ? maxParallelForks.toInteger() : null
109123
userIgnoreFailures = project.hasProperty('ignoreFailures') ? ignoreFailures : false
@@ -359,7 +373,7 @@ subprojects {
359373
// The suites are for running sets of tests in IDEs.
360374
// Gradle will run each test class, so we exclude the suites to avoid redundantly running the tests twice.
361375
def testsToExclude = ['**/*Suite.class']
362-
// Exclude PowerMock tests when running with Java 16 until a version of PowerMock that supports Java 16 is released
376+
// Exclude PowerMock tests when running with Java 16 or newer until a version of PowerMock that supports the relevant versions is released
363377
// The relevant issues are https://github.com/powermock/powermock/issues/1094 and https://github.com/powermock/powermock/issues/1099
364378
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_16)) {
365379
testsToExclude.addAll([
@@ -372,8 +386,7 @@ subprojects {
372386
"**/WorkerSinkTaskTest.*", "**/WorkerSinkTaskThreadedTest.*", "**/WorkerSourceTaskTest.*",
373387
"**/WorkerTaskTest.*", "**/WorkerTest.*", "**/RestServerTest.*",
374388
// streams tests
375-
"**/KafkaStreamsTest.*", "**/RepartitionTopicsTest.*", "**/RocksDBMetricsRecorderTest.*",
376-
"**/StreamsMetricsImplTest.*", "**/StateManagerUtilTest.*", "**/TableSourceNodeTest.*"
389+
"**/KafkaStreamsTest.*"
377390
])
378391
}
379392

docs/upgrade.html

+6-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@
1919

2020
<script id="upgrade-template" type="text/x-handlebars-template">
2121

22-
<h5><a id="upgrade_300_notable" href="#upgrade_300_notable">Notable changes in 3.0.0</a></h5>
22+
<h5><a id="upgrade_310_notable" href="#upgrade_310_notable">Notable changes in 3.1.0</a></h5>
23+
<ul>
24+
<li>Apache Kafka supports Java 17.</li>
25+
</ul>
26+
27+
<h5><a id="upgrade_310_notable" href="#upgrade_300_notable">Notable changes in 3.0.0</a></h5>
2328
<ul>
2429
<li>ZooKeeper has been upgraded to version 3.6.3.</li>
2530
<li>A preview of KRaft mode is available, though upgrading to it from the 2.8 Early Access release is not possible. See

gradle/dependencies.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ versions += [
6262
checkstyle: "8.36.2",
6363
commonsCli: "1.4",
6464
dropwizardMetrics: "4.1.12.1",
65-
gradle: "7.1.1",
65+
gradle: "7.2",
6666
grgit: "4.1.0",
6767
httpclient: "4.5.13",
6868
easymock: "4.3",
@@ -100,7 +100,7 @@ versions += [
100100
lz4: "1.7.1",
101101
mavenArtifact: "3.8.1",
102102
metrics: "2.2.0",
103-
mockito: "3.9.0",
103+
mockito: "3.12.4",
104104
netty: "4.1.62.Final",
105105
powermock: "2.0.9",
106106
reflections: "0.9.12",
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionSha256Sum=9bb8bc05f562f2d42bdf1ba8db62f6b6fa1c3bf6c392228802cc7cb0578fe7e0
4-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-all.zip
3+
distributionSha256Sum=a8da5b02437a60819cad23e10fc7e9cf32bcb57029d9cb277e26eeff76ce014b
4+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
55
zipStoreBase=GRADLE_USER_HOME
66
zipStorePath=wrapper/dists

gradlew

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ case "`uname`" in
7272
Darwin* )
7373
darwin=true
7474
;;
75-
MINGW* )
75+
MSYS* | MINGW* )
7676
msys=true
7777
;;
7878
NONSTOP* )
@@ -84,7 +84,7 @@ esac
8484
# Loop in case we encounter an error.
8585
for attempt in 1 2 3; do
8686
if [ ! -e "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" ]; then
87-
if ! curl -s -S --retry 3 -L -o "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" "https://raw.githubusercontent.com/gradle/gradle/v7.1.1/gradle/wrapper/gradle-wrapper.jar"; then
87+
if ! curl -s -S --retry 3 -L -o "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" "https://raw.githubusercontent.com/gradle/gradle/v7.2.0/gradle/wrapper/gradle-wrapper.jar"; then
8888
rm -f "$APP_HOME/gradle/wrapper/gradle-wrapper.jar"
8989
# Pause for a bit before looping in case the server throttled us.
9090
sleep 5

raft/src/test/java/org/apache/kafka/raft/KafkaRaftClientTest.java

+17-36
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,7 @@ public void testRejectVotesFromSameEpochAfterResigningLeadership() throws Except
101101
int epoch = 2;
102102

103103
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
104-
.updateRandom(random -> {
105-
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
106-
})
104+
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
107105
.withElectedLeader(epoch, localId)
108106
.build();
109107

@@ -126,9 +124,7 @@ public void testRejectVotesFromSameEpochAfterResigningCandidacy() throws Excepti
126124
int epoch = 2;
127125

128126
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
129-
.updateRandom(random -> {
130-
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
131-
})
127+
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
132128
.withVotedCandidate(epoch, localId)
133129
.build();
134130

@@ -151,11 +147,9 @@ public void testGrantVotesFromHigherEpochAfterResigningLeadership() throws Excep
151147
int epoch = 2;
152148

153149
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
154-
.updateRandom(random -> {
155-
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
156-
})
157-
.withElectedLeader(epoch, localId)
158-
.build();
150+
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
151+
.withElectedLeader(epoch, localId)
152+
.build();
159153

160154
// Resign from leader, will restart in resigned state
161155
assertTrue(context.client.quorum().isResigned());
@@ -181,11 +175,9 @@ public void testGrantVotesFromHigherEpochAfterResigningCandidacy() throws Except
181175
int epoch = 2;
182176

183177
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
184-
.updateRandom(random -> {
185-
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
186-
})
187-
.withVotedCandidate(epoch, localId)
188-
.build();
178+
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
179+
.withVotedCandidate(epoch, localId)
180+
.build();
189181

190182
// Resign from candidate, will restart in candidate state
191183
assertTrue(context.client.quorum().isCandidate());
@@ -235,11 +227,9 @@ public void testInitializeAsResignedAndBecomeCandidate() throws Exception {
235227
int epoch = 2;
236228

237229
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
238-
.updateRandom(random -> {
239-
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
240-
})
241-
.withElectedLeader(epoch, localId)
242-
.build();
230+
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
231+
.withElectedLeader(epoch, localId)
232+
.build();
243233

244234
// Resign from leader, will restart in resigned state
245235
assertTrue(context.client.quorum().isResigned());
@@ -262,9 +252,7 @@ public void testInitializeAsResignedLeaderFromStateStore() throws Exception {
262252
int epoch = 2;
263253

264254
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
265-
.updateRandom(random -> {
266-
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
267-
})
255+
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
268256
.withElectedLeader(epoch, localId)
269257
.build();
270258

@@ -728,9 +716,7 @@ public void testEndQuorumIgnoredAsCandidateIfOlderEpoch() throws Exception {
728716
Set<Integer> voters = Utils.mkSet(localId, otherNodeId);
729717

730718
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
731-
.updateRandom(random -> {
732-
Mockito.doReturn(jitterMs).when(random).nextInt(Mockito.anyInt());
733-
})
719+
.updateRandom(r -> r.mockNextInt(jitterMs))
734720
.withUnknownLeader(epoch - 1)
735721
.build();
736722

@@ -1238,9 +1224,7 @@ public void testRetryElection() throws Exception {
12381224
Set<Integer> voters = Utils.mkSet(localId, otherNodeId);
12391225

12401226
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
1241-
.updateRandom(random -> {
1242-
Mockito.doReturn(exponentialFactor).when(random).nextInt(Mockito.anyInt());
1243-
})
1227+
.updateRandom(r -> r.mockNextInt(exponentialFactor))
12441228
.build();
12451229

12461230
context.assertUnknownLeader(0);
@@ -2184,9 +2168,7 @@ public void testFetchShouldBeTreatedAsLeaderAcknowledgement() throws Exception {
21842168
Set<Integer> voters = Utils.mkSet(localId, otherNodeId);
21852169

21862170
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
2187-
.updateRandom(random -> {
2188-
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
2189-
})
2171+
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
21902172
.withUnknownLeader(epoch - 1)
21912173
.build();
21922174

@@ -2395,9 +2377,7 @@ public void testClusterAuthorizationFailedInBeginQuorumEpoch() throws Exception
23952377
Set<Integer> voters = Utils.mkSet(localId, otherNodeId);
23962378

23972379
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
2398-
.updateRandom(random -> {
2399-
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
2400-
})
2380+
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
24012381
.withUnknownLeader(epoch - 1)
24022382
.build();
24032383

@@ -2799,4 +2779,5 @@ public void testObserverFetchWithNoLocalId() throws Exception {
27992779
private static KafkaMetric getMetric(final Metrics metrics, final String name) {
28002780
return metrics.metrics().get(metrics.metricName(name, "raft-metrics"));
28012781
}
2782+
28022783
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.kafka.raft;
18+
19+
import java.util.OptionalInt;
20+
import java.util.Random;
21+
import java.util.function.IntFunction;
22+
23+
/**
24+
* A Random instance that makes it easy to modify the behavior of certain methods for test purposes.
25+
*/
26+
class MockableRandom extends Random {
27+
28+
private IntFunction<OptionalInt> nextIntFunction = __ -> OptionalInt.empty();
29+
30+
public MockableRandom(long seed) {
31+
super(seed);
32+
}
33+
34+
public void mockNextInt(int expectedBound, int returnValue) {
35+
this.nextIntFunction = b -> {
36+
if (b == expectedBound)
37+
return OptionalInt.of(returnValue);
38+
else
39+
return OptionalInt.empty();
40+
};
41+
}
42+
43+
public void mockNextInt(int returnValue) {
44+
this.nextIntFunction = __ -> OptionalInt.of(returnValue);
45+
}
46+
47+
@Override
48+
public int nextInt(int bound) {
49+
return nextIntFunction.apply(bound).orElse(super.nextInt(bound));
50+
}
51+
}

0 commit comments

Comments
 (0)