diff --git a/pom.xml b/pom.xml
index bff266a4..2475c9b4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -37,7 +37,7 @@
1.17.1
5.10.0
1.0.0
- 2.21.1
+ 2.22.0
1.5.0-b01
3.12.1
3.3.0
@@ -46,7 +46,7 @@
3.2.3
1.1.0
UTF-8
- 3.25.0
+ 3.25.1
1.5.3
0.16
1.17
diff --git a/src/main/java/org/qortal/ApplyBootstrap.java b/src/main/java/org/qortal/ApplyBootstrap.java
new file mode 100644
index 00000000..9b370f7a
--- /dev/null
+++ b/src/main/java/org/qortal/ApplyBootstrap.java
@@ -0,0 +1,227 @@
+package org.qortal;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
+import org.qortal.api.ApiKey;
+import org.qortal.api.ApiRequest;
+import org.qortal.controller.BootstrapNode;
+import org.qortal.settings.Settings;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.security.Security;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static org.qortal.controller.BootstrapNode.AGENTLIB_JVM_HOLDER_ARG;
+
+public class ApplyBootstrap {
+
+ static {
+ // This static block will be called before others if using ApplyBootstrap.main()
+
+ // Log into different files for bootstrap - this has to be before LogManger.getLogger() calls
+ System.setProperty("log4j2.filenameTemplate", "log-apply-bootstrap.txt");
+
+ // This must go before any calls to LogManager/Logger
+ System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
+ }
+
+ private static final Logger LOGGER = LogManager.getLogger(ApplyBootstrap.class);
+ private static final String JAR_FILENAME = BootstrapNode.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 = "";
+
+ private static final long CHECK_INTERVAL = 15 * 1000L; // ms
+ private static final int MAX_ATTEMPTS = 20;
+
+ public static void main(String[] args) {
+ Security.insertProviderAt(new BouncyCastleProvider(), 0);
+ Security.insertProviderAt(new BouncyCastleJsseProvider(), 1);
+
+ // Load/check settings, which potentially sets up blockchain config, etc.
+ if (args.length > 0)
+ Settings.fileInstance(args[0]);
+ else
+ Settings.getInstance();
+
+ LOGGER.info("Applying bootstrap...");
+
+ // Shutdown node using API
+ if (!shutdownNode())
+ return;
+
+ // Delete db
+ deleteDB();
+
+ // Restart node
+ restartNode(args);
+
+ LOGGER.info("Bootstrapping...");
+ }
+
+ private static boolean shutdownNode() {
+ String baseUri = "http://localhost:" + Settings.getInstance().getApiPort() + "/";
+ LOGGER.info(() -> String.format("Shutting down node using API via %s", baseUri));
+
+ // The /admin/stop endpoint requires an API key, which may or may not be already generated
+ boolean apiKeyNewlyGenerated = false;
+ ApiKey apiKey = null;
+ try {
+ apiKey = new ApiKey();
+ if (!apiKey.generated()) {
+ apiKey.generate();
+ apiKeyNewlyGenerated = true;
+ LOGGER.info("Generated API key");
+ }
+ } catch (IOException e) {
+ LOGGER.info("Error loading API key: {}", e.getMessage());
+ }
+
+ // Create GET params
+ Map params = new HashMap<>();
+ if (apiKey != null) {
+ params.put("apiKey", apiKey.toString());
+ }
+
+ // Attempt to stop the node
+ int attempt;
+ for (attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
+ 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", params);
+ if (response == null) {
+ // No response - consider node shut down
+ if (apiKeyNewlyGenerated) {
+ // API key was newly generated for bootstrapping node, so we need to remove it
+ ApplyBootstrap.removeGeneratedApiKey();
+ }
+ return true;
+ }
+
+ LOGGER.info(() -> String.format("Response from API: %s", response));
+
+ try {
+ Thread.sleep(CHECK_INTERVAL);
+ } catch (InterruptedException e) {
+ // We still need to check...
+ break;
+ }
+ }
+
+ if (apiKeyNewlyGenerated) {
+ // API key was newly generated for bootstrapping node, so we need to remove it
+ ApplyBootstrap.removeGeneratedApiKey();
+ }
+
+ if (attempt == MAX_ATTEMPTS) {
+ LOGGER.error("Failed to shutdown node - giving up");
+ return false;
+ }
+
+ return true;
+ }
+
+ private static void removeGeneratedApiKey() {
+ try {
+ LOGGER.info("Removing newly generated API key...");
+
+ // Delete the API key since it was only generated for bootstrapping node
+ ApiKey apiKey = new ApiKey();
+ apiKey.delete();
+
+ } catch (IOException e) {
+ LOGGER.info("Error loading or deleting API key: {}", e.getMessage());
+ }
+ }
+
+ private static void deleteDB() {
+ // Get the repository path from settings
+ String repositoryPath = Settings.getInstance().getRepositoryPath();
+ LOGGER.debug(String.format("Repository path: %s", repositoryPath));
+
+ try {
+ Path directory = Paths.get(repositoryPath);
+ Files.walkFileTree(directory, new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } catch (IOException e) {
+ LOGGER.error("Error deleting DB: {}", e.getMessage());
+ }
+ }
+
+ private static void restartNode(String[] args) {
+ String javaHome = System.getProperty("java.home");
+ LOGGER.debug(() -> String.format("Java home: %s", javaHome));
+
+ Path javaBinary = Paths.get(javaHome, "bin", "java");
+ LOGGER.debug(() -> String.format("Java binary: %s", javaBinary));
+
+ Path exeLauncher = Paths.get(WINDOWS_EXE_LAUNCHER);
+ LOGGER.debug(() -> String.format("Windows EXE launcher: %s", exeLauncher));
+
+ List 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());
+
+ // Reapply any retained, but disabled, -agentlib JVM arg
+ javaCmd = javaCmd.stream()
+ .map(arg -> arg.replace(AGENTLIB_JVM_HOLDER_ARG, "-agentlib"))
+ .collect(Collectors.toList());
+
+ // Call mainClass in JAR
+ javaCmd.addAll(Arrays.asList("-jar", JAR_FILENAME));
+
+ // Add saved command-line args
+ javaCmd.addAll(Arrays.asList(args));
+ }
+
+ try {
+ LOGGER.info(String.format("Restarting node with: %s", String.join(" ", javaCmd)));
+
+ ProcessBuilder processBuilder = new ProcessBuilder(javaCmd);
+
+ if (Files.exists(exeLauncher)) {
+ LOGGER.debug(() -> 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 process will inherit our stdout and stderr
+ processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
+ processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT);
+
+ Process process = processBuilder.start();
+
+ // Nothing to pipe to new process, so close output stream (process's stdin)
+ process.getOutputStream().close();
+ } catch (Exception e) {
+ LOGGER.error(String.format("Failed to restart node (BAD): %s", e.getMessage()));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/qortal/ApplyRestart.java b/src/main/java/org/qortal/ApplyRestart.java
new file mode 100644
index 00000000..70d07df5
--- /dev/null
+++ b/src/main/java/org/qortal/ApplyRestart.java
@@ -0,0 +1,196 @@
+package org.qortal;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
+import org.qortal.api.ApiKey;
+import org.qortal.api.ApiRequest;
+import org.qortal.controller.RestartNode;
+import org.qortal.settings.Settings;
+
+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.security.Security;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static org.qortal.controller.RestartNode.AGENTLIB_JVM_HOLDER_ARG;
+
+public class ApplyRestart {
+
+ static {
+ // This static block will be called before others if using ApplyRestart.main()
+
+ // Log into different files for restart node - this has to be before LogManger.getLogger() calls
+ System.setProperty("log4j2.filenameTemplate", "log-apply-restart.txt");
+
+ // This must go before any calls to LogManager/Logger
+ System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
+ }
+
+ private static final Logger LOGGER = LogManager.getLogger(ApplyRestart.class);
+ private static final String JAR_FILENAME = RestartNode.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 = "";
+
+ private static final long CHECK_INTERVAL = 10 * 1000L; // ms
+ private static final int MAX_ATTEMPTS = 12;
+
+ public static void main(String[] args) {
+ Security.insertProviderAt(new BouncyCastleProvider(), 0);
+ Security.insertProviderAt(new BouncyCastleJsseProvider(), 1);
+
+ // Load/check settings, which potentially sets up blockchain config, etc.
+ if (args.length > 0)
+ Settings.fileInstance(args[0]);
+ else
+ Settings.getInstance();
+
+ LOGGER.info("Applying restart...");
+
+ // Shutdown node using API
+ if (!shutdownNode())
+ return;
+
+ // Restart node
+ restartNode(args);
+
+ LOGGER.info("Restarting...");
+ }
+
+ private static boolean shutdownNode() {
+ String baseUri = "http://localhost:" + Settings.getInstance().getApiPort() + "/";
+ LOGGER.info(() -> String.format("Shutting down node using API via %s", baseUri));
+
+ // The /admin/stop endpoint requires an API key, which may or may not be already generated
+ boolean apiKeyNewlyGenerated = false;
+ ApiKey apiKey = null;
+ try {
+ apiKey = new ApiKey();
+ if (!apiKey.generated()) {
+ apiKey.generate();
+ apiKeyNewlyGenerated = true;
+ LOGGER.info("Generated API key");
+ }
+ } catch (IOException e) {
+ LOGGER.info("Error loading API key: {}", e.getMessage());
+ }
+
+ // Create GET params
+ Map params = new HashMap<>();
+ if (apiKey != null) {
+ params.put("apiKey", apiKey.toString());
+ }
+
+ // Attempt to stop the node
+ int attempt;
+ for (attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
+ 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", params);
+ if (response == null) {
+ // No response - consider node shut down
+ if (apiKeyNewlyGenerated) {
+ // API key was newly generated for restarting node, so we need to remove it
+ ApplyRestart.removeGeneratedApiKey();
+ }
+ return true;
+ }
+
+ LOGGER.info(() -> String.format("Response from API: %s", response));
+
+ try {
+ Thread.sleep(CHECK_INTERVAL);
+ } catch (InterruptedException e) {
+ // We still need to check...
+ break;
+ }
+ }
+
+ if (apiKeyNewlyGenerated) {
+ // API key was newly generated for restarting node, so we need to remove it
+ ApplyRestart.removeGeneratedApiKey();
+ }
+
+ if (attempt == MAX_ATTEMPTS) {
+ LOGGER.error("Failed to shutdown node - giving up");
+ return false;
+ }
+
+ return true;
+ }
+
+ private static void removeGeneratedApiKey() {
+ try {
+ LOGGER.info("Removing newly generated API key...");
+
+ // Delete the API key since it was only generated for restarting node
+ ApiKey apiKey = new ApiKey();
+ apiKey.delete();
+
+ } catch (IOException e) {
+ LOGGER.info("Error loading or deleting API key: {}", e.getMessage());
+ }
+ }
+
+ private static void restartNode(String[] args) {
+ String javaHome = System.getProperty("java.home");
+ LOGGER.debug(() -> String.format("Java home: %s", javaHome));
+
+ Path javaBinary = Paths.get(javaHome, "bin", "java");
+ LOGGER.debug(() -> String.format("Java binary: %s", javaBinary));
+
+ Path exeLauncher = Paths.get(WINDOWS_EXE_LAUNCHER);
+ LOGGER.debug(() -> String.format("Windows EXE launcher: %s", exeLauncher));
+
+ List 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());
+
+ // Reapply any retained, but disabled, -agentlib JVM arg
+ javaCmd = javaCmd.stream()
+ .map(arg -> arg.replace(AGENTLIB_JVM_HOLDER_ARG, "-agentlib"))
+ .collect(Collectors.toList());
+
+ // Call mainClass in JAR
+ javaCmd.addAll(Arrays.asList("-jar", JAR_FILENAME));
+
+ // Add saved command-line args
+ javaCmd.addAll(Arrays.asList(args));
+ }
+
+ try {
+ LOGGER.debug(String.format("Restarting node with: %s", String.join(" ", javaCmd)));
+
+ ProcessBuilder processBuilder = new ProcessBuilder(javaCmd);
+
+ if (Files.exists(exeLauncher)) {
+ LOGGER.debug(() -> 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 process will inherit our stdout and stderr
+ processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
+ processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT);
+
+ Process process = processBuilder.start();
+
+ // Nothing to pipe to new process, so close output stream (process's stdin)
+ process.getOutputStream().close();
+ } catch (Exception e) {
+ LOGGER.error(String.format("Failed to restart node (BAD): %s", e.getMessage()));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/qortal/ApplyUpdate.java b/src/main/java/org/qortal/ApplyUpdate.java
index f1f9d22f..ba2e0860 100644
--- a/src/main/java/org/qortal/ApplyUpdate.java
+++ b/src/main/java/org/qortal/ApplyUpdate.java
@@ -38,7 +38,7 @@ public class ApplyUpdate {
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 String JAVA_TOOL_OPTIONS_VALUE = "";
private static final long CHECK_INTERVAL = 30 * 1000L; // ms
private static final int MAX_ATTEMPTS = 12;
@@ -139,7 +139,7 @@ public class ApplyUpdate {
apiKey.delete();
} catch (IOException e) {
- LOGGER.info("Error loading or deleting API key: {}", e.getMessage());
+ LOGGER.error("Error loading or deleting API key: {}", e.getMessage());
}
}
@@ -181,13 +181,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.debug(() -> String.format("Java home: %s", javaHome));
Path javaBinary = Paths.get(javaHome, "bin", "java");
- LOGGER.info(() -> String.format("Java binary: %s", javaBinary));
+ LOGGER.debug(() -> String.format("Java binary: %s", javaBinary));
Path exeLauncher = Paths.get(WINDOWS_EXE_LAUNCHER);
- LOGGER.info(() -> String.format("Windows EXE launcher: %s", exeLauncher));
+ LOGGER.debug(() -> String.format("Windows EXE launcher: %s", exeLauncher));
List javaCmd;
if (Files.exists(exeLauncher)) {
@@ -213,12 +213,12 @@ public class ApplyUpdate {
}
try {
- LOGGER.info(String.format("Restarting node with: %s", String.join(" ", javaCmd)));
+ LOGGER.debug(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));
+ LOGGER.debug(() -> 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);
}
diff --git a/src/main/java/org/qortal/api/restricted/resource/AdminResource.java b/src/main/java/org/qortal/api/restricted/resource/AdminResource.java
index 35bf9108..837288e5 100644
--- a/src/main/java/org/qortal/api/restricted/resource/AdminResource.java
+++ b/src/main/java/org/qortal/api/restricted/resource/AdminResource.java
@@ -24,8 +24,9 @@ import org.qortal.api.model.ActivitySummary;
import org.qortal.api.model.NodeInfo;
import org.qortal.api.model.NodeStatus;
import org.qortal.block.BlockChain;
-import org.qortal.controller.AutoUpdate;
+import org.qortal.controller.BootstrapNode;
import org.qortal.controller.Controller;
+import org.qortal.controller.RestartNode;
import org.qortal.controller.Synchronizer;
import org.qortal.controller.Synchronizer.SynchronizationResult;
import org.qortal.controller.repository.BlockArchiveRebuilder;
@@ -250,7 +251,7 @@ public class AdminResource {
// Not important
}
- AutoUpdate.attemptRestart();
+ RestartNode.attemptToRestart();
}).start();
@@ -281,7 +282,7 @@ public class AdminResource {
// Not important
}
- AutoUpdate.attemptBootstrap();
+ BootstrapNode.attemptToBootstrap();
}).start();
diff --git a/src/main/java/org/qortal/controller/AutoUpdate.java b/src/main/java/org/qortal/controller/AutoUpdate.java
index 0bdcfd48..4b315651 100644
--- a/src/main/java/org/qortal/controller/AutoUpdate.java
+++ b/src/main/java/org/qortal/controller/AutoUpdate.java
@@ -23,12 +23,9 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.lang.management.ManagementFactory;
import java.nio.ByteBuffer;
-import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
@@ -294,171 +291,4 @@ public class AutoUpdate extends Thread {
return true; // repo was okay, even if applying update failed
}
}
-
- public static boolean attemptRestart() {
- LOGGER.info(String.format("Restarting node..."));
-
- // Give repository a chance to backup in case things go badly wrong (if enabled)
- if (Settings.getInstance().getRepositoryBackupInterval() > 0) {
- try {
- // Timeout if the database isn't ready for backing up after 60 seconds
- long timeout = 60 * 1000L;
- RepositoryManager.backup(true, "backup", timeout);
-
- } catch (TimeoutException e) {
- LOGGER.info("Attempt to backup repository failed due to timeout: {}", e.getMessage());
- // Continue with the node restart anyway...
- }
- }
-
- // Call ApplyUpdate to end this process (unlocking current JAR so it can be replaced)
- String javaHome = System.getProperty("java.home");
- LOGGER.debug(String.format("Java home: %s", javaHome));
-
- Path javaBinary = Paths.get(javaHome, "bin", "java");
- LOGGER.debug(String.format("Java binary: %s", javaBinary));
-
- try {
- List javaCmd = new ArrayList<>();
- // Java runtime binary itself
- javaCmd.add(javaBinary.toString());
-
- // JVM arguments
- javaCmd.addAll(ManagementFactory.getRuntimeMXBean().getInputArguments());
-
- // Disable, but retain, any -agentlib JVM arg as sub-process might fail if it tries to reuse same port
- javaCmd = javaCmd.stream()
- .map(arg -> arg.replace("-agentlib", AGENTLIB_JVM_HOLDER_ARG))
- .collect(Collectors.toList());
-
- // Remove JNI options as they won't be supported by command-line 'java'
- // These are typically added by the AdvancedInstaller Java launcher EXE
- javaCmd.removeAll(Arrays.asList("abort", "exit", "vfprintf"));
-
- // Call ApplyUpdate using JAR
- javaCmd.addAll(Arrays.asList("-cp", JAR_FILENAME, ApplyUpdate.class.getCanonicalName()));
-
- // Add command-line args saved from start-up
- String[] savedArgs = Controller.getInstance().getSavedArgs();
- if (savedArgs != null)
- javaCmd.addAll(Arrays.asList(savedArgs));
-
- LOGGER.info(String.format("Restarting node with: %s", String.join(" ", javaCmd)));
-
- SysTray.getInstance().showMessage(Translator.INSTANCE.translate("SysTray", "AUTO_UPDATE"), //TODO
- Translator.INSTANCE.translate("SysTray", "APPLYING_UPDATE_AND_RESTARTING"), //TODO
- MessageType.INFO);
-
- ProcessBuilder processBuilder = new ProcessBuilder(javaCmd);
-
- // New process will inherit our stdout and stderr
- processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
- processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT);
-
- Process process = processBuilder.start();
-
- // Nothing to pipe to new process, so close output stream (process's stdin)
- process.getOutputStream().close();
-
- return true; // restarting node OK
- } catch (Exception e) {
- LOGGER.error(String.format("Failed to restart node: %s", e.getMessage()));
-
- return true; // repo was okay, even if applying update failed
- }
- }
-
- public static boolean attemptBootstrap() {
- LOGGER.info(String.format("Bootstrapping node..."));
-
- // Give repository a chance to backup in case things go badly wrong (if enabled)
- if (Settings.getInstance().getRepositoryBackupInterval() > 0) {
- try {
- // Timeout if the database isn't ready for backing up after 60 seconds
- long timeout = 60 * 1000L;
- RepositoryManager.backup(true, "backup", timeout);
-
- } catch (TimeoutException e) {
- LOGGER.info("Attempt to backup repository failed due to timeout: {}", e.getMessage());
- // Continue with the bootstrap anyway...
- }
- }
-
- // Get the repository path from settings
- String repositoryPath = Settings.getInstance().getRepositoryPath();
- LOGGER.debug(String.format("Repository path: %s", repositoryPath));
-
- // Call ApplyUpdate to end this process (unlocking current JAR so it can be replaced)
- String javaHome = System.getProperty("java.home");
- LOGGER.debug(String.format("Java home: %s", javaHome));
-
- Path javaBinary = Paths.get(javaHome, "bin", "java");
- LOGGER.debug(String.format("Java binary: %s", javaBinary));
-
- try {
- Path directory = Paths.get(repositoryPath);
- Files.walkFileTree(directory, new SimpleFileVisitor() {
- @Override
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
- Files.delete(file);
- return FileVisitResult.CONTINUE;
- }
-
- @Override
- public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
- Files.delete(dir);
- return FileVisitResult.CONTINUE;
- }
- });
-
- List javaCmd = new ArrayList<>();
-
- // Java runtime binary itself
- javaCmd.add(javaBinary.toString());
-
- // JVM arguments
- javaCmd.addAll(ManagementFactory.getRuntimeMXBean().getInputArguments());
-
- // Disable, but retain, any -agentlib JVM arg as sub-process might fail if it tries to reuse same port
- javaCmd = javaCmd.stream()
- .map(arg -> arg.replace("-agentlib", AGENTLIB_JVM_HOLDER_ARG))
- .collect(Collectors.toList());
-
- // Remove JNI options as they won't be supported by command-line 'java'
- // These are typically added by the AdvancedInstaller Java launcher EXE
- javaCmd.removeAll(Arrays.asList("abort", "exit", "vfprintf"));
-
- // Call ApplyUpdate using JAR
- javaCmd.addAll(Arrays.asList("-cp", JAR_FILENAME, ApplyUpdate.class.getCanonicalName()));
-
- // Add command-line args saved from start-up
- String[] savedArgs = Controller.getInstance().getSavedArgs();
- if (savedArgs != null)
- javaCmd.addAll(Arrays.asList(savedArgs));
-
- LOGGER.info(String.format("Restarting node with: %s", String.join(" ", javaCmd)));
-
- SysTray.getInstance().showMessage(Translator.INSTANCE.translate("SysTray", "AUTO_UPDATE"), //TODO
- Translator.INSTANCE.translate("SysTray", "APPLYING_UPDATE_AND_RESTARTING"), //TODO
- MessageType.INFO);
-
- ProcessBuilder processBuilder = new ProcessBuilder(javaCmd);
-
- // New process will inherit our stdout and stderr
- processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
- processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT);
-
- Process process = processBuilder.start();
-
- // Nothing to pipe to new process, so close output stream (process's stdin)
- process.getOutputStream().close();
-
- return true; // restarting node OK
- } catch (Exception e) {
- LOGGER.error(String.format("Failed to restart node: %s", e.getMessage()));
-
- return true; // repo was okay, even if applying update failed
- }
- }
-
}
diff --git a/src/main/java/org/qortal/controller/BootstrapNode.java b/src/main/java/org/qortal/controller/BootstrapNode.java
new file mode 100644
index 00000000..9d0f8b36
--- /dev/null
+++ b/src/main/java/org/qortal/controller/BootstrapNode.java
@@ -0,0 +1,103 @@
+package org.qortal.controller;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.qortal.ApplyBootstrap;
+import org.qortal.globalization.Translator;
+import org.qortal.gui.SysTray;
+import org.qortal.repository.RepositoryManager;
+import org.qortal.settings.Settings;
+
+import java.awt.TrayIcon.MessageType;
+import java.lang.management.ManagementFactory;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.stream.Collectors;
+
+/* NOTE: It is CRITICAL that we use OpenJDK and not Java SE because our uber jar repacks BouncyCastle which, in turn, unsigns BC causing it to be rejected as a security provider by Java SE. */
+
+public class BootstrapNode {
+
+ public static final String JAR_FILENAME = "qortal.jar";
+ public static final String AGENTLIB_JVM_HOLDER_ARG = "-DQORTAL_agentlib=";
+
+ private static final Logger LOGGER = LogManager.getLogger(BootstrapNode.class);
+
+ public static boolean attemptToBootstrap() {
+ LOGGER.info(String.format("Bootstrapping node..."));
+
+ // Give repository a chance to backup in case things go badly wrong (if enabled)
+ if (Settings.getInstance().getRepositoryBackupInterval() > 0) {
+ try {
+ // Timeout if the database isn't ready for backing up after 60 seconds
+ long timeout = 60 * 1000L;
+ RepositoryManager.backup(true, "backup", timeout);
+
+ } catch (TimeoutException e) {
+ LOGGER.info("Attempt to backup repository failed due to timeout: {}", e.getMessage());
+ // Continue with the bootstrap anyway...
+ }
+ }
+
+ // Call ApplyBootstrap to end this process
+ String javaHome = System.getProperty("java.home");
+ LOGGER.debug(String.format("Java home: %s", javaHome));
+
+ Path javaBinary = Paths.get(javaHome, "bin", "java");
+ LOGGER.debug(String.format("Java binary: %s", javaBinary));
+
+ try {
+ List javaCmd = new ArrayList<>();
+
+ // Java runtime binary itself
+ javaCmd.add(javaBinary.toString());
+
+ // JVM arguments
+ javaCmd.addAll(ManagementFactory.getRuntimeMXBean().getInputArguments());
+
+ // Disable, but retain, any -agentlib JVM arg as sub-process might fail if it tries to reuse same port
+ javaCmd = javaCmd.stream()
+ .map(arg -> arg.replace("-agentlib", AGENTLIB_JVM_HOLDER_ARG))
+ .collect(Collectors.toList());
+
+ // Remove JNI options as they won't be supported by command-line 'java'
+ // These are typically added by the AdvancedInstaller Java launcher EXE
+ javaCmd.removeAll(Arrays.asList("abort", "exit", "vfprintf"));
+
+ // Call ApplyBootstrap using JAR
+ javaCmd.addAll(Arrays.asList("-cp", JAR_FILENAME, ApplyBootstrap.class.getCanonicalName()));
+
+ // Add command-line args saved from start-up
+ String[] savedArgs = Controller.getInstance().getSavedArgs();
+ if (savedArgs != null)
+ javaCmd.addAll(Arrays.asList(savedArgs));
+
+ LOGGER.info(String.format("Restarting node with: %s", String.join(" ", javaCmd)));
+
+ SysTray.getInstance().showMessage(Translator.INSTANCE.translate("SysTray", "BOOTSTRAP_NODE"),
+ Translator.INSTANCE.translate("SysTray", "APPLYING_BOOTSTRAP_AND_RESTARTING"),
+ MessageType.INFO);
+
+ ProcessBuilder processBuilder = new ProcessBuilder(javaCmd);
+
+ // New process will inherit our stdout and stderr
+ processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
+ processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT);
+
+ Process process = processBuilder.start();
+
+ // Nothing to pipe to new process, so close output stream (process's stdin)
+ process.getOutputStream().close();
+
+ return true; // restarting node OK
+ } catch (Exception e) {
+ LOGGER.error(String.format("Failed to restart node: %s", e.getMessage()));
+
+ return true; // repo was okay, even if applying bootstrap failed
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/qortal/controller/RestartNode.java b/src/main/java/org/qortal/controller/RestartNode.java
new file mode 100644
index 00000000..b8d81b85
--- /dev/null
+++ b/src/main/java/org/qortal/controller/RestartNode.java
@@ -0,0 +1,102 @@
+package org.qortal.controller;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.qortal.ApplyRestart;;
+import org.qortal.globalization.Translator;
+import org.qortal.gui.SysTray;
+import org.qortal.repository.RepositoryManager;
+import org.qortal.settings.Settings;
+
+import java.awt.TrayIcon.MessageType;
+import java.lang.management.ManagementFactory;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.stream.Collectors;
+
+/* NOTE: It is CRITICAL that we use OpenJDK and not Java SE because our uber jar repacks BouncyCastle which, in turn, unsigns BC causing it to be rejected as a security provider by Java SE. */
+
+public class RestartNode {
+
+ public static final String JAR_FILENAME = "qortal.jar";
+ public static final String AGENTLIB_JVM_HOLDER_ARG = "-DQORTAL_agentlib=";
+
+ private static final Logger LOGGER = LogManager.getLogger(RestartNode.class);
+
+ public static boolean attemptToRestart() {
+ LOGGER.info(String.format("Restarting node..."));
+
+ // Give repository a chance to backup in case things go badly wrong (if enabled)
+ if (Settings.getInstance().getRepositoryBackupInterval() > 0) {
+ try {
+ // Timeout if the database isn't ready for backing up after 60 seconds
+ long timeout = 60 * 1000L;
+ RepositoryManager.backup(true, "backup", timeout);
+
+ } catch (TimeoutException e) {
+ LOGGER.info("Attempt to backup repository failed due to timeout: {}", e.getMessage());
+ // Continue with the node restart anyway...
+ }
+ }
+
+ // Call ApplyRestart to end this process
+ String javaHome = System.getProperty("java.home");
+ LOGGER.debug(String.format("Java home: %s", javaHome));
+
+ Path javaBinary = Paths.get(javaHome, "bin", "java");
+ LOGGER.debug(String.format("Java binary: %s", javaBinary));
+
+ try {
+ List javaCmd = new ArrayList<>();
+ // Java runtime binary itself
+ javaCmd.add(javaBinary.toString());
+
+ // JVM arguments
+ javaCmd.addAll(ManagementFactory.getRuntimeMXBean().getInputArguments());
+
+ // Disable, but retain, any -agentlib JVM arg as sub-process might fail if it tries to reuse same port
+ javaCmd = javaCmd.stream()
+ .map(arg -> arg.replace("-agentlib", AGENTLIB_JVM_HOLDER_ARG))
+ .collect(Collectors.toList());
+
+ // Remove JNI options as they won't be supported by command-line 'java'
+ // These are typically added by the AdvancedInstaller Java launcher EXE
+ javaCmd.removeAll(Arrays.asList("abort", "exit", "vfprintf"));
+
+ // Call ApplyRestart using JAR
+ javaCmd.addAll(Arrays.asList("-cp", JAR_FILENAME, ApplyRestart.class.getCanonicalName()));
+
+ // Add command-line args saved from start-up
+ String[] savedArgs = Controller.getInstance().getSavedArgs();
+ if (savedArgs != null)
+ javaCmd.addAll(Arrays.asList(savedArgs));
+
+ LOGGER.debug(String.format("Restarting node with: %s", String.join(" ", javaCmd)));
+
+ SysTray.getInstance().showMessage(Translator.INSTANCE.translate("SysTray", "RESTARTING_NODE"),
+ Translator.INSTANCE.translate("SysTray", "APPLYING_RESTARTING_NODE"),
+ MessageType.INFO);
+
+ ProcessBuilder processBuilder = new ProcessBuilder(javaCmd);
+
+ // New process will inherit our stdout and stderr
+ processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
+ processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT);
+
+ Process process = processBuilder.start();
+
+ // Nothing to pipe to new process, so close output stream (process's stdin)
+ process.getOutputStream().close();
+
+ return true; // restarting node OK
+ } catch (Exception e) {
+ LOGGER.error(String.format("Failed to restart node: %s", e.getMessage()));
+
+ return true; // repo was okay, even if applying restart failed
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/i18n/SysTray_de.properties b/src/main/resources/i18n/SysTray_de.properties
index c92130f1..4f09a3a2 100644
--- a/src/main/resources/i18n/SysTray_de.properties
+++ b/src/main/resources/i18n/SysTray_de.properties
@@ -46,3 +46,11 @@ SYNCHRONIZE_CLOCK = Synchronisiere Uhrzeit
SYNCHRONIZING_BLOCKCHAIN = Synchronisiere
SYNCHRONIZING_CLOCK = Uhrzeit wird synchronisiert
+
+RESTARTING_NODE = Knoten wird neu gestartet
+
+APPLYING_RESTARTING_NODE = Neustart des knotens wird angewendet. Bitte haben Sie Geduld...
+
+BOOTSTRAP_NODE = Bootstrapping-Knoten
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = Bootstrap anwenden und Knoten neu starten. Bitte haben Sie Geduld...
diff --git a/src/main/resources/i18n/SysTray_en.properties b/src/main/resources/i18n/SysTray_en.properties
index 39940be0..afc8210c 100644
--- a/src/main/resources/i18n/SysTray_en.properties
+++ b/src/main/resources/i18n/SysTray_en.properties
@@ -46,3 +46,11 @@ SYNCHRONIZE_CLOCK = Synchronize clock
SYNCHRONIZING_BLOCKCHAIN = Synchronizing
SYNCHRONIZING_CLOCK = Synchronizing clock
+
+RESTARTING_NODE = Restarting Node
+
+APPLYING_RESTARTING_NODE = Applying restarting node. Please be patient...
+
+BOOTSTRAP_NODE = Bootstrapping Node
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = Applying bootstrap and restarting node. Please be patient...
diff --git a/src/main/resources/i18n/SysTray_es.properties b/src/main/resources/i18n/SysTray_es.properties
index 36cbb22c..cc0ba7b2 100644
--- a/src/main/resources/i18n/SysTray_es.properties
+++ b/src/main/resources/i18n/SysTray_es.properties
@@ -44,3 +44,11 @@ SYNCHRONIZE_CLOCK = Sincronizar reloj
SYNCHRONIZING_BLOCKCHAIN = Sincronizando
SYNCHRONIZING_CLOCK = Sincronizando reloj
+
+RESTARTING_NODE = Reiniciando el nodo
+
+APPLYING_RESTARTING_NODE = Aplicando el nodo de reinicio. Por favor sea paciente...
+
+BOOTSTRAP_NODE = Nodo de arranque
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = Aplicando bootstrap y reiniciando el nodo. Por favor sea paciente...
diff --git a/src/main/resources/i18n/SysTray_fi.properties b/src/main/resources/i18n/SysTray_fi.properties
index 4038d615..351bc921 100644
--- a/src/main/resources/i18n/SysTray_fi.properties
+++ b/src/main/resources/i18n/SysTray_fi.properties
@@ -46,3 +46,11 @@ SYNCHRONIZE_CLOCK = Synkronisoi kello
SYNCHRONIZING_BLOCKCHAIN = Synkronisoi
SYNCHRONIZING_CLOCK = Synkronisoi kelloa
+
+RESTARTING_NODE = Käynnistetään uudelleen solmu
+
+APPLYING_RESTARTING_NODE = Käytetään uudelleenkäynnistyssolmua. Olkaa kärsivällisiä...
+
+BOOTSTRAP_NODE = Käynnistyssolmu
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = Käynnistetään ja käynnistetään solmu uudelleen. Olkaa kärsivällisiä...
diff --git a/src/main/resources/i18n/SysTray_fr.properties b/src/main/resources/i18n/SysTray_fr.properties
index 2e376842..39ac44e3 100644
--- a/src/main/resources/i18n/SysTray_fr.properties
+++ b/src/main/resources/i18n/SysTray_fr.properties
@@ -46,3 +46,11 @@ SYNCHRONIZE_CLOCK = Mettre l'heure à jour
SYNCHRONIZING_BLOCKCHAIN = Synchronisation
SYNCHRONIZING_CLOCK = Synchronisation de l'heure
+
+RESTARTING_NODE = Redémarrage du nœud
+
+APPLYING_RESTARTING_NODE = Application du redémarrage du nœud. S'il vous plaît, soyez patient...
+
+BOOTSTRAP_NODE = Nœud d'amorçage
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = Application du bootstrap et redémarrage du nœud. S'il vous plaît, soyez patient...
diff --git a/src/main/resources/i18n/SysTray_he.properties b/src/main/resources/i18n/SysTray_he.properties
index 50ef8933..09a9f6dd 100644
--- a/src/main/resources/i18n/SysTray_he.properties
+++ b/src/main/resources/i18n/SysTray_he.properties
@@ -46,3 +46,11 @@ SYNCHRONIZE_CLOCK = סנכרן שעון
SYNCHRONIZING_BLOCKCHAIN = מסנכרן
SYNCHRONIZING_CLOCK = מסנכרן שעון
+
+RESTARTING_NODE = הפעלה מחדש של צומת
+
+APPLYING_RESTARTING_NODE = החלת צומת הפעלה מחדש. אנא התאזר בסבלנות...
+
+BOOTSTRAP_NODE = צומת אתחול
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = החלת אתחול והפעלת צומת
diff --git a/src/main/resources/i18n/SysTray_hu.properties b/src/main/resources/i18n/SysTray_hu.properties
index 74ab21ac..3b1da2cc 100644
--- a/src/main/resources/i18n/SysTray_hu.properties
+++ b/src/main/resources/i18n/SysTray_hu.properties
@@ -46,3 +46,11 @@ SYNCHRONIZE_CLOCK = Óra-szinkronizálás megkezdése
SYNCHRONIZING_BLOCKCHAIN = Szinkronizálás
SYNCHRONIZING_CLOCK = Óraszinkronizálás folyamatban
+
+RESTARTING_NODE = Csomópont újraindítása
+
+APPLYING_RESTARTING_NODE = Újraindító csomópont alkalmazása. Kérjük várjon...
+
+BOOTSTRAP_NODE = Rendszerindítási csomópont
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = Bootstrap alkalmazása és csomópont újraindítása. Kérjük várjon...
diff --git a/src/main/resources/i18n/SysTray_it.properties b/src/main/resources/i18n/SysTray_it.properties
index d966d825..55cd4c83 100644
--- a/src/main/resources/i18n/SysTray_it.properties
+++ b/src/main/resources/i18n/SysTray_it.properties
@@ -46,3 +46,11 @@ SYNCHRONIZE_CLOCK = Sincronizzare l'orologio
SYNCHRONIZING_BLOCKCHAIN = Sincronizzazione della blockchain
SYNCHRONIZING_CLOCK = Sincronizzazione orologio
+
+RESTARTING_NODE = Riavvio del nodo
+
+APPLYING_RESTARTING_NODE = Applicazione del nodo di riavvio. Per favore sii paziente...
+
+BOOTSTRAP_NODE = Nodo di bootstrap
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = Applicazione del bootstrap e riavvio del nodo. Per favore sii paziente...
diff --git a/src/main/resources/i18n/SysTray_jp.properties b/src/main/resources/i18n/SysTray_jp.properties
index c4cccb5b..036bf8f2 100644
--- a/src/main/resources/i18n/SysTray_jp.properties
+++ b/src/main/resources/i18n/SysTray_jp.properties
@@ -46,3 +46,11 @@ SYNCHRONIZE_CLOCK = 時刻を同期
SYNCHRONIZING_BLOCKCHAIN = ブロックチェーンを同期中
SYNCHRONIZING_CLOCK = 時刻を同期中
+
+RESTARTING_NODE = ノードを再起動しています
+
+APPLYING_RESTARTING_NODE = 再起動ノードを適用しています。 しばらくお待ちください...
+
+BOOTSTRAP_NODE = ブートストラップ ノード
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = ブートストラップを適用し、ノードを再起動します。 しばらくお待ちください...
diff --git a/src/main/resources/i18n/SysTray_ko.properties b/src/main/resources/i18n/SysTray_ko.properties
index dc6cb69b..0db49c53 100644
--- a/src/main/resources/i18n/SysTray_ko.properties
+++ b/src/main/resources/i18n/SysTray_ko.properties
@@ -46,3 +46,11 @@ SYNCHRONIZE_CLOCK = 시간 동기화
SYNCHRONIZING_BLOCKCHAIN = 동기화중
SYNCHRONIZING_CLOCK = 시간 동기화
+
+RESTARTING_NODE = 노드 다시 시작 중
+
+APPLYING_RESTARTING_NODE = 노드 재시작을 적용합니다. 기다려주십시오...
+
+BOOTSTRAP_NODE = 부트스트래핑 노드
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = 부트스트랩을 적용하고 노드를 다시 시작합니다. 기다려주십시오...
diff --git a/src/main/resources/i18n/SysTray_nl.properties b/src/main/resources/i18n/SysTray_nl.properties
index 3d7de024..776720d5 100644
--- a/src/main/resources/i18n/SysTray_nl.properties
+++ b/src/main/resources/i18n/SysTray_nl.properties
@@ -46,3 +46,11 @@ SYNCHRONIZE_CLOCK = Synchronizeer klok
SYNCHRONIZING_BLOCKCHAIN = Bezig met synchronizeren
SYNCHRONIZING_CLOCK = Klok wordt gesynchronizeerd
+
+RESTARTING_NODE = Knooppunt opnieuw starten
+
+APPLYING_RESTARTING_NODE = Herstartknooppunt toepassen. Wees alstublieft geduldig...
+
+BOOTSTRAP_NODE = Opstartknooppunt
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = Bootstrap toepassen en knooppunt opnieuw starten. Wees alstublieft geduldig...
diff --git a/src/main/resources/i18n/SysTray_pl.properties b/src/main/resources/i18n/SysTray_pl.properties
index 84740da0..8ee40751 100644
--- a/src/main/resources/i18n/SysTray_pl.properties
+++ b/src/main/resources/i18n/SysTray_pl.properties
@@ -44,3 +44,11 @@ SYNCHRONIZE_CLOCK = Synchronizuj zegar
SYNCHRONIZING_BLOCKCHAIN = Synchronizacja
SYNCHRONIZING_CLOCK = Synchronizacja zegara
+
+RESTARTING_NODE = Ponowne uruchamianie węzła
+
+APPLYING_RESTARTING_NODE = Stosuję ponowne uruchomienie węzła. Proszę być cierpliwym...
+
+BOOTSTRAP_NODE = Węzeł ładowania początkowego
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = Stosowanie ładowania początkowego i ponowne uruchamianie węzła. Proszę być cierpliwym...
diff --git a/src/main/resources/i18n/SysTray_ro.properties b/src/main/resources/i18n/SysTray_ro.properties
index 4130bbcb..f3cf76c9 100644
--- a/src/main/resources/i18n/SysTray_ro.properties
+++ b/src/main/resources/i18n/SysTray_ro.properties
@@ -46,3 +46,11 @@ SYNCHRONIZE_CLOCK = Sincronizare ceas
SYNCHRONIZING_BLOCKCHAIN = Sincronizare
SYNCHRONIZING_CLOCK = Se sincronizeaza ceasul
+
+RESTARTING_NODE = Repornirea nodului
+
+APPLYING_RESTARTING_NODE = Se aplica nodul de repornire. Te rog fii rabdator...
+
+BOOTSTRAP_NODE = Nod de bootstrap
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = Se aplica bootstrap si se reporneste nodul. Te rog fii rabdator...
diff --git a/src/main/resources/i18n/SysTray_ru.properties b/src/main/resources/i18n/SysTray_ru.properties
index c8615f73..ddf0c0a0 100644
--- a/src/main/resources/i18n/SysTray_ru.properties
+++ b/src/main/resources/i18n/SysTray_ru.properties
@@ -46,3 +46,11 @@ SYNCHRONIZE_CLOCK = Синхронизировать время
SYNCHRONIZING_BLOCKCHAIN = Синхронизация цепи
SYNCHRONIZING_CLOCK = Проверка времени
+
+RESTARTING_NODE = Перезапуск узла
+
+APPLYING_RESTARTING_NODE = Применение перезапуска узла. Пожалуйста, будьте терпеливы...
+
+BOOTSTRAP_NODE = Узел начальной загрузки
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = Применение начальной загрузки и перезапуск узла. Пожалуйста, будьте терпеливы...
diff --git a/src/main/resources/i18n/SysTray_sv.properties b/src/main/resources/i18n/SysTray_sv.properties
index 96f291b5..a38cca3a 100644
--- a/src/main/resources/i18n/SysTray_sv.properties
+++ b/src/main/resources/i18n/SysTray_sv.properties
@@ -44,3 +44,11 @@ SYNCHRONIZE_CLOCK = Synkronisera klockan
SYNCHRONIZING_BLOCKCHAIN = Synkroniserar
SYNCHRONIZING_CLOCK = Synkroniserar klockan
+
+RESTARTING_NODE = Mimitian deui Node
+
+APPLYING_RESTARTING_NODE = Nerapkeun titik ngamimitian deui. Mangga sing sabar...
+
+BOOTSTRAP_NODE = Bootstrapping Node
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = Nerapkeun bootstrap sareng hurungkeun deui titik. Mangga sing sabar...
diff --git a/src/main/resources/i18n/SysTray_zh_CN.properties b/src/main/resources/i18n/SysTray_zh_CN.properties
index d6848a7c..7e84f3f2 100644
--- a/src/main/resources/i18n/SysTray_zh_CN.properties
+++ b/src/main/resources/i18n/SysTray_zh_CN.properties
@@ -46,3 +46,11 @@ SYNCHRONIZE_CLOCK = 同步时钟
SYNCHRONIZING_BLOCKCHAIN = 正在同步区块链
SYNCHRONIZING_CLOCK = 正在同步时钟
+
+RESTARTING_NODE = 重新启动节点
+
+APPLYING_RESTARTING_NODE = 应用重新启动节点。 请耐心等待...
+
+BOOTSTRAP_NODE = 引导节点
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = 应用引导程序并重新启动节点。 请耐心等待...
diff --git a/src/main/resources/i18n/SysTray_zh_TW.properties b/src/main/resources/i18n/SysTray_zh_TW.properties
index eabdbb63..d774bc11 100644
--- a/src/main/resources/i18n/SysTray_zh_TW.properties
+++ b/src/main/resources/i18n/SysTray_zh_TW.properties
@@ -46,3 +46,11 @@ SYNCHRONIZE_CLOCK = 同步時鐘
SYNCHRONIZING_BLOCKCHAIN = 正在同步區塊鏈
SYNCHRONIZING_CLOCK = 正在同步時鐘
+
+RESTARTING_NODE = 重新啟動節點
+
+APPLYING_RESTARTING_NODE = 應用重新啟動節點。 請耐心等待...
+
+BOOTSTRAP_NODE = 引導節點
+
+APPLYING_BOOTSTRAP_AND_RESTARTING = 應用引導程式並重新啟動節點。 請耐心等待...