Browse Source

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.
pull/16/head
catbref 4 years ago
parent
commit
f8725d6313
  1. 41
      src/main/java/org/qortal/ApplyUpdate.java
  2. 63
      src/test/java/org/qortal/test/apps/LaunchExeWIthJvmOptions.java

41
src/main/java/org/qortal/ApplyUpdate.java

@ -35,6 +35,8 @@ public class ApplyUpdate {
private static final String JAR_FILENAME = AutoUpdate.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 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 int MAX_ATTEMPTS = 12;
@ -65,17 +67,19 @@ public class ApplyUpdate {
}
private static boolean shutdownNode() {
String BASE_URI = "http://localhost:" + Settings.getInstance().getApiPort() + "/";
LOGGER.info(String.format("Shutting down node using API via %s", BASE_URI));
String baseUri = "http://localhost:" + Settings.getInstance().getApiPort() + "/";
LOGGER.info(() -> String.format("Shutting down node using API via %s", baseUri));
int attempt;
for (attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
LOGGER.info(String.format("Attempt #%d out of %d to shutdown node", attempt + 1, MAX_ATTEMPTS));
String response = ApiRequest.perform(BASE_URI + "admin/stop", null);
final int attemptForLogging = attempt;
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)
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 {
Thread.sleep(CHECK_INTERVAL);
@ -99,19 +103,20 @@ public class ApplyUpdate {
Path newJar = Paths.get(NEW_JAR_FILENAME);
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;
}
int 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 {
Files.copy(newJar, realJar, StandardCopyOption.REPLACE_EXISTING);
break;
} 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
}
@ -119,6 +124,7 @@ public class ApplyUpdate {
try {
Thread.sleep(CHECK_INTERVAL);
} catch (InterruptedException e) {
LOGGER.warn("Ignoring interrupt...");
// Doggedly retry
}
}
@ -129,13 +135,13 @@ public class ApplyUpdate {
private static void restartNode(String[] args) {
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");
LOGGER.info(String.format("Java binary: %s", javaBinary));
LOGGER.info(() -> String.format("Java binary: %s", javaBinary));
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;
if (Files.exists(exeLauncher)) {
@ -156,9 +162,16 @@ public class ApplyUpdate {
}
try {
LOGGER.info(String.format("Restarting node with: %s", String.join(" ", javaCmd)));
LOGGER.info(() -> String.format("Restarting node with: %s", String.join(" ", javaCmd)));
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);
}
new ProcessBuilder(javaCmd).start();
processBuilder.start();
} catch (IOException e) {
LOGGER.error(String.format("Failed to restart node (BAD): %s", e.getMessage()));
}

63
src/test/java/org/qortal/test/apps/LaunchExeWIthJvmOptions.java

@ -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…
Cancel
Save