Skip to content

Commit 2f31a3d

Browse files
committed
fix Flink runner tests on Java 21
1 parent 477c747 commit 2f31a3d

3 files changed

Lines changed: 61 additions & 13 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"comment": "Modify this file in a trivial way to cause this test suite to run.",
3-
"modification": 1
3+
"modification": 6
44
}

runners/flink/flink_runner.gradle

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,30 @@ if (use_override) {
180180
}
181181
}
182182

183+
def flinkTestJvmArgs() {
184+
def testJavaVer = project.findProperty('testJavaVersion') ? (project.property('testJavaVersion') as int) : JavaVersion.current().majorVersion.toInteger()
185+
if (testJavaVer >= 17) {
186+
return [
187+
"--add-opens=java.base/sun.nio.ch=ALL-UNNAMED",
188+
"--add-opens=java.base/java.nio=ALL-UNNAMED",
189+
"--add-opens=java.base/java.util=ALL-UNNAMED",
190+
"--add-opens=java.base/java.lang.invoke=ALL-UNNAMED",
191+
"--add-opens=java.base/java.lang=ALL-UNNAMED",
192+
"-Djava.security.manager=allow",
193+
]
194+
} else {
195+
return []
196+
}
197+
}
198+
183199
test {
184200
systemProperty "log4j.configuration", "log4j-test.properties"
185201
// Change log level to debug:
186202
// systemProperty "org.slf4j.simpleLogger.defaultLogLevel", "debug"
187203
// Change log level to debug only for the package and nested packages:
188204
// systemProperty "org.slf4j.simpleLogger.log.org.apache.beam.runners.flink.translation.wrappers.streaming", "debug"
189205
jvmArgs "-XX:-UseGCOverheadLimit"
206+
jvmArgs += flinkTestJvmArgs()
190207
if (System.getProperty("beamSurefireArgline")) {
191208
jvmArgs System.getProperty("beamSurefireArgline")
192209
}

runners/flink/src/test/java/org/apache/beam/runners/flink/FlinkSubmissionTest.java

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import java.io.File;
2121
import java.lang.reflect.Field;
22-
import java.lang.reflect.Modifier;
22+
import java.lang.reflect.Method;
2323
import java.nio.charset.StandardCharsets;
2424
import java.nio.file.Files;
2525
import java.security.Permission;
@@ -230,21 +230,52 @@ private static void restoreEnvironment() throws Exception {
230230
* We modify the JVM's environment variables here. This is necessary for the end-to-end test
231231
* because Flink's CliFrontend requires a Flink configuration file for which the location can only
232232
* be set using the {@code ConfigConstants.ENV_FLINK_CONF_DIR} environment variable.
233+
*
234+
* <p>On Unix JDKs, {@code ProcessEnvironment.theEnvironment} is a {@code Map<Variable,Value>}.
235+
* {@code System.getenv(String)} looks up {@code Variable} keys, so inserting plain {@code String}
236+
* keys (via {@code putAll}) is a no-op for lookups and leaves Flink unable to find {@code
237+
* FLINK_CONF_DIR}. On Windows the map uses {@code String} keys (plus a case-insensitive map).
233238
*/
239+
@SuppressWarnings("unchecked")
234240
private static void modifyEnv(Map<String, String> env) throws Exception {
235-
Class processEnv = Class.forName("java.lang.ProcessEnvironment");
236-
Field envField = processEnv.getDeclaredField("theUnmodifiableEnvironment");
241+
Class<?> processEnvironment = Class.forName("java.lang.ProcessEnvironment");
237242

238-
Field modifiersField = Field.class.getDeclaredField("modifiers");
239-
modifiersField.setAccessible(true);
240-
modifiersField.setInt(envField, envField.getModifiers() & ~Modifier.FINAL);
241-
242-
envField.setAccessible(true);
243-
envField.set(null, env);
244-
envField.setAccessible(false);
243+
Field theEnvironmentField = processEnvironment.getDeclaredField("theEnvironment");
244+
theEnvironmentField.setAccessible(true);
245+
Map<Object, Object> envMap = (Map<Object, Object>) theEnvironmentField.get(null);
246+
envMap.clear();
247+
try {
248+
// Unix: keys/values are ProcessEnvironment$Variable / $Value, not String.
249+
Class<?> variableClass = Class.forName("java.lang.ProcessEnvironment$Variable");
250+
Class<?> valueClass = Class.forName("java.lang.ProcessEnvironment$Value");
251+
Method valueOfVariable = variableClass.getDeclaredMethod("valueOf", String.class);
252+
Method valueOfValue = valueClass.getDeclaredMethod("valueOf", String.class);
253+
valueOfVariable.setAccessible(true);
254+
valueOfValue.setAccessible(true);
255+
for (Map.Entry<String, String> entry : env.entrySet()) {
256+
envMap.put(
257+
valueOfVariable.invoke(null, entry.getKey()),
258+
valueOfValue.invoke(null, entry.getValue()));
259+
}
260+
} catch (ClassNotFoundException e) {
261+
// Windows (and similar): theEnvironment uses String keys.
262+
envMap.putAll(env);
263+
}
245264

246-
modifiersField.setInt(envField, envField.getModifiers() & Modifier.FINAL);
247-
modifiersField.setAccessible(false);
265+
// Windows: System.getenv(String) reads the case-insensitive map when present.
266+
try {
267+
Field theCaseInsensitiveEnvironmentField =
268+
processEnvironment.getDeclaredField("theCaseInsensitiveEnvironment");
269+
theCaseInsensitiveEnvironmentField.setAccessible(true);
270+
Map<String, String> ciEnvMap =
271+
(Map<String, String>) theCaseInsensitiveEnvironmentField.get(null);
272+
if (ciEnvMap != null) {
273+
ciEnvMap.clear();
274+
ciEnvMap.putAll(env);
275+
}
276+
} catch (NoSuchFieldException e) {
277+
// Not present on Unix JDKs.
278+
}
248279
}
249280

250281
/** Prevents the CliFrontend from calling System.exit. */

0 commit comments

Comments
 (0)