有一些方法可以为当前运行的虚拟机设置环境变量,例如:
private static void setEnv(Map<String, String> newEnv) throws Exception {
Map<String, String> env = System.getenv();
Class<?> cl = env.getClass();
Field field = cl.getDeclaredField("m");
field.setAccessible(true);
@SuppressWarnings("unchecked")
Map<String, String> envMap = (Map<String, String>) field.get(env);
envMap.putAll(newEnv);
}
(这个想法来自How do I set environment variables from Java?的答案)
但在我的情况下,我需要环境变量来影响在 VM 外部执行的库,所以这种方法不能解决我的问题。
经过一番思考,我意识到我想为 JVM 的父进程设置环境,所以我需要首先设置所需的变量,然后递归地运行另一个将执行我的应用程序的 JVM 实例——然后是变量即使它们的代码在 VM 之外执行,也会影响这些库。
所以逻辑应该是这样的:
if (required vars are absent) {
start a process that {
set required vars;
run another instance of the JVM with the application inside;
}
exit;
}
// here the vars already set
do whatever we need in the proper environment
对于 Java,代码可能如下所示:
public class SecondVM {
public static void main(String[] args) {
if ( System.getenv("SWT_GTK3") == null
|| System.getenv("LIBOVERLAY_SCROLLBAR") == null )
{
URL classResource = SecondVM.class.getResource("SecondVM.class");
boolean fromJar = classResource.getProtocol().equals("rsrc");
String exePath = ClassLoader.getSystemClassLoader().getResource(".").getPath();
exePath = new File(exePath).getAbsolutePath().replaceFirst("\\.$", "").replaceFirst("bin$", "");
if (!exePath.endsWith(System.getProperty("file.separator")))
exePath += System.getProperty("file.separator");
String[] script = {
"/bin/bash", "-c",
"export SWT_GTK3=0; "
+ "export LIBOVERLAY_SCROLLBAR=0; "
+ (fromJar? // TODO: Put the proper paths, packages and class names here
"java -jar " + exePath + "SecondVM.jar" : // if runs from jar
"java -cp ./bin/:../ExtLibs/swt_linux64/swt.jar " // if runs from under Eclipse or somewhat alike
+ "com.m_v.test.SecondVM")
};
try {
Process p = new ProcessBuilder(script).start();
// When jar is run from a bash script, it kills the second VM when exits.
// Let it has some time to take a breath
p.waitFor(12, TimeUnit.HOURS);
} catch (Exception e) { e.printStackTrace(); }
System.exit(0);
}
// Now the env vars are OK. We can use SWT with normal scrollbars
Display display = Display.getDefault();
// .... do watever we need
}
}
如果从 shell 脚本运行 jar,我们必须等待子进程完成,然后才能退出原始进程,因此这种解决方案会导致同时运行两个 JVM 实例的开销。如果不需要提供从脚本运行它的可能性,p.waitFor(12, TimeUnit.HOURS); 可以替换为p.waitFor(12, TimeUnit.MILLISECONDS);,或者,也许,完全删除(我没有测试过没有它),所以我们可以有一个实例JVM,就像普通的 Java 程序一样。
带有text 小部件和scrollbar 的工作sn-p 位于http://ideone.com/eRjePQ