forked from Qortal/qortal
Modify ApplyUpdate to pass JVM options to Windows launcher EXE
ApplyUpdate is the 2nd-stage of the auto-update system, called after core has downloaded the update. As old versions of the Windows launcher EXE selects a 'client' JVM mode, heap memory could be limited to only 256MB. Until users upgrade via Windows installer, which replaces the EXE with 'server' JVM mode baked-in, then a work-around is to pass -XX:MaxRAMFraction=4 to the new JVM in order to emulate heap size in 'server' JVM mode.
This commit is contained in:
parent
2165c87b9d
commit
f8725d6313
@ -35,6 +35,8 @@ public class ApplyUpdate {
|
|||||||
private static final String JAR_FILENAME = AutoUpdate.JAR_FILENAME;
|
private static final String JAR_FILENAME = AutoUpdate.JAR_FILENAME;
|
||||||
private static final String NEW_JAR_FILENAME = AutoUpdate.NEW_JAR_FILENAME;
|
private static final String NEW_JAR_FILENAME = AutoUpdate.NEW_JAR_FILENAME;
|
||||||
private static final String WINDOWS_EXE_LAUNCHER = "qortal.exe";
|
private static final String WINDOWS_EXE_LAUNCHER = "qortal.exe";
|
||||||
|
private static final String JAVA_TOOL_OPTIONS_NAME = "JAVA_TOOL_OPTIONS";
|
||||||
|
private static final String JAVA_TOOL_OPTIONS_VALUE = "-XX:MaxRAMFraction=4";
|
||||||
|
|
||||||
private static final long CHECK_INTERVAL = 10 * 1000L; // ms
|
private static final long CHECK_INTERVAL = 10 * 1000L; // ms
|
||||||
private static final int MAX_ATTEMPTS = 12;
|
private static final int MAX_ATTEMPTS = 12;
|
||||||
@ -65,17 +67,19 @@ public class ApplyUpdate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean shutdownNode() {
|
private static boolean shutdownNode() {
|
||||||
String BASE_URI = "http://localhost:" + Settings.getInstance().getApiPort() + "/";
|
String baseUri = "http://localhost:" + Settings.getInstance().getApiPort() + "/";
|
||||||
LOGGER.info(String.format("Shutting down node using API via %s", BASE_URI));
|
LOGGER.info(() -> String.format("Shutting down node using API via %s", baseUri));
|
||||||
|
|
||||||
int attempt;
|
int attempt;
|
||||||
for (attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
|
for (attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
|
||||||
LOGGER.info(String.format("Attempt #%d out of %d to shutdown node", attempt + 1, MAX_ATTEMPTS));
|
final int attemptForLogging = attempt;
|
||||||
String response = ApiRequest.perform(BASE_URI + "admin/stop", null);
|
LOGGER.info(() -> String.format("Attempt #%d out of %d to shutdown node", attemptForLogging + 1, MAX_ATTEMPTS));
|
||||||
|
String response = ApiRequest.perform(baseUri + "admin/stop", null);
|
||||||
if (response == null)
|
if (response == null)
|
||||||
break;
|
// No response - consider node shut down
|
||||||
|
return true;
|
||||||
|
|
||||||
LOGGER.info(String.format("Response from API: %s", response));
|
LOGGER.info(() -> String.format("Response from API: %s", response));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(CHECK_INTERVAL);
|
Thread.sleep(CHECK_INTERVAL);
|
||||||
@ -99,19 +103,20 @@ public class ApplyUpdate {
|
|||||||
Path newJar = Paths.get(NEW_JAR_FILENAME);
|
Path newJar = Paths.get(NEW_JAR_FILENAME);
|
||||||
|
|
||||||
if (!Files.exists(newJar)) {
|
if (!Files.exists(newJar)) {
|
||||||
LOGGER.warn(String.format("Replacement JAR '%s' not found?", newJar));
|
LOGGER.warn(() -> String.format("Replacement JAR '%s' not found?", newJar));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int attempt;
|
int attempt;
|
||||||
for (attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
|
for (attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
|
||||||
LOGGER.info(String.format("Attempt #%d out of %d to replace JAR", attempt + 1, MAX_ATTEMPTS));
|
final int attemptForLogging = attempt;
|
||||||
|
LOGGER.info(() -> String.format("Attempt #%d out of %d to replace JAR", attemptForLogging + 1, MAX_ATTEMPTS));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Files.copy(newJar, realJar, StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(newJar, realJar, StandardCopyOption.REPLACE_EXISTING);
|
||||||
break;
|
break;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOGGER.info(String.format("Unable to replace JAR: %s", e.getMessage()));
|
LOGGER.info(() -> String.format("Unable to replace JAR: %s", e.getMessage()));
|
||||||
|
|
||||||
// Try again
|
// Try again
|
||||||
}
|
}
|
||||||
@ -119,6 +124,7 @@ public class ApplyUpdate {
|
|||||||
try {
|
try {
|
||||||
Thread.sleep(CHECK_INTERVAL);
|
Thread.sleep(CHECK_INTERVAL);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
|
LOGGER.warn("Ignoring interrupt...");
|
||||||
// Doggedly retry
|
// Doggedly retry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,13 +135,13 @@ public class ApplyUpdate {
|
|||||||
|
|
||||||
private static void restartNode(String[] args) {
|
private static void restartNode(String[] args) {
|
||||||
String javaHome = System.getProperty("java.home");
|
String javaHome = System.getProperty("java.home");
|
||||||
LOGGER.info(String.format("Java home: %s", javaHome));
|
LOGGER.info(() -> String.format("Java home: %s", javaHome));
|
||||||
|
|
||||||
Path javaBinary = Paths.get(javaHome, "bin", "java");
|
Path javaBinary = Paths.get(javaHome, "bin", "java");
|
||||||
LOGGER.info(String.format("Java binary: %s", javaBinary));
|
LOGGER.info(() -> String.format("Java binary: %s", javaBinary));
|
||||||
|
|
||||||
Path exeLauncher = Paths.get(WINDOWS_EXE_LAUNCHER);
|
Path exeLauncher = Paths.get(WINDOWS_EXE_LAUNCHER);
|
||||||
LOGGER.info(String.format("Windows EXE launcher: %s", exeLauncher));
|
LOGGER.info(() -> String.format("Windows EXE launcher: %s", exeLauncher));
|
||||||
|
|
||||||
List<String> javaCmd;
|
List<String> javaCmd;
|
||||||
if (Files.exists(exeLauncher)) {
|
if (Files.exists(exeLauncher)) {
|
||||||
@ -156,9 +162,16 @@ public class ApplyUpdate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
LOGGER.info(String.format("Restarting node with: %s", String.join(" ", javaCmd)));
|
LOGGER.info(() -> String.format("Restarting node with: %s", String.join(" ", javaCmd)));
|
||||||
|
|
||||||
new ProcessBuilder(javaCmd).start();
|
ProcessBuilder processBuilder = new ProcessBuilder(javaCmd);
|
||||||
|
|
||||||
|
if (Files.exists(exeLauncher)) {
|
||||||
|
LOGGER.info(() -> String.format("Setting env %s to %s", JAVA_TOOL_OPTIONS_NAME, JAVA_TOOL_OPTIONS_VALUE));
|
||||||
|
processBuilder.environment().put(JAVA_TOOL_OPTIONS_NAME, JAVA_TOOL_OPTIONS_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
processBuilder.start();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOGGER.error(String.format("Failed to restart node (BAD): %s", e.getMessage()));
|
LOGGER.error(String.format("Failed to restart node (BAD): %s", e.getMessage()));
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
package org.qortal.test.apps;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class LaunchExeWIthJvmOptions {
|
||||||
|
|
||||||
|
private static final String JAR_FILENAME = "qortal.jar";
|
||||||
|
private static final String WINDOWS_EXE_LAUNCHER = "qortal.exe";
|
||||||
|
private static final String JAVA_TOOL_OPTIONS_NAME = "JAVA_TOOL_OPTIONS";
|
||||||
|
private static final String JAVA_TOOL_OPTIONS_VALUE = "-XX:MaxRAMFraction=4";
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String javaHome = System.getProperty("java.home");
|
||||||
|
System.out.println(String.format("Java home: %s", javaHome));
|
||||||
|
|
||||||
|
Path javaBinary = Paths.get(javaHome, "bin", "java");
|
||||||
|
System.out.println(String.format("Java binary: %s", javaBinary));
|
||||||
|
|
||||||
|
Path exeLauncher = Paths.get(WINDOWS_EXE_LAUNCHER);
|
||||||
|
System.out.println(String.format("Windows EXE launcher: %s", exeLauncher));
|
||||||
|
|
||||||
|
List<String> javaCmd;
|
||||||
|
if (Files.exists(exeLauncher)) {
|
||||||
|
javaCmd = Arrays.asList(exeLauncher.toString());
|
||||||
|
} else {
|
||||||
|
javaCmd = new ArrayList<>();
|
||||||
|
// Java runtime binary itself
|
||||||
|
javaCmd.add(javaBinary.toString());
|
||||||
|
|
||||||
|
// JVM arguments
|
||||||
|
javaCmd.addAll(ManagementFactory.getRuntimeMXBean().getInputArguments());
|
||||||
|
|
||||||
|
// Call mainClass in JAR
|
||||||
|
javaCmd.addAll(Arrays.asList("-jar", JAR_FILENAME));
|
||||||
|
|
||||||
|
// Add saved command-line args
|
||||||
|
javaCmd.addAll(Arrays.asList(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
System.out.println(String.format("Restarting node with: %s", String.join(" ", javaCmd)));
|
||||||
|
|
||||||
|
ProcessBuilder processBuilder = new ProcessBuilder(javaCmd);
|
||||||
|
|
||||||
|
if (Files.exists(exeLauncher)) {
|
||||||
|
System.out.println(String.format("Setting env %s to %s", JAVA_TOOL_OPTIONS_NAME, JAVA_TOOL_OPTIONS_VALUE));
|
||||||
|
processBuilder.environment().put(JAVA_TOOL_OPTIONS_NAME, JAVA_TOOL_OPTIONS_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
processBuilder.start();
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println(String.format("Failed to restart node (BAD): %s", e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user