|
19 | 19 |
|
20 | 20 | import java.io.File; |
21 | 21 | import java.lang.reflect.Field; |
22 | | -import java.lang.reflect.Modifier; |
| 22 | +import java.lang.reflect.Method; |
23 | 23 | import java.nio.charset.StandardCharsets; |
24 | 24 | import java.nio.file.Files; |
25 | 25 | import java.security.Permission; |
@@ -230,21 +230,52 @@ private static void restoreEnvironment() throws Exception { |
230 | 230 | * We modify the JVM's environment variables here. This is necessary for the end-to-end test |
231 | 231 | * because Flink's CliFrontend requires a Flink configuration file for which the location can only |
232 | 232 | * 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). |
233 | 238 | */ |
| 239 | + @SuppressWarnings("unchecked") |
234 | 240 | 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"); |
237 | 242 |
|
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 | + } |
245 | 264 |
|
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 | + } |
248 | 279 | } |
249 | 280 |
|
250 | 281 | /** Prevents the CliFrontend from calling System.exit. */ |
|
0 commit comments