Subject: [PATCH] #23355
---
Index: core/src/org/openstreetmap/josm/gui/MainApplication.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/core/src/org/openstreetmap/josm/gui/MainApplication.java b/core/src/org/openstreetmap/josm/gui/MainApplication.java
--- a/core/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 18917)
+++ b/core/src/org/openstreetmap/josm/gui/MainApplication.java	(date 1703018564751)
@@ -57,6 +57,7 @@
 import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
+import javax.swing.JTextPane;
 import javax.swing.KeyStroke;
 import javax.swing.LookAndFeel;
 import javax.swing.RepaintManager;
@@ -129,6 +130,7 @@
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.util.RedirectInputMap;
 import org.openstreetmap.josm.gui.util.WindowGeometry;
+import org.openstreetmap.josm.gui.widgets.TextContextualPopupMenu;
 import org.openstreetmap.josm.gui.widgets.UrlLabel;
 import org.openstreetmap.josm.io.CachedFile;
 import org.openstreetmap.josm.io.CertificateAmendment;
@@ -405,6 +407,33 @@
         // CHECKSTYLE.ON: LineLength
     }
 
+    /**
+     * Tells the user that a sanity check failed
+     * @param title The title of the message to show
+     * @param canContinue {@code true} if the failed sanity check(s) will not instantly kill JOSM when the user edits
+     * @param message The message parts to show the user (as a list)
+     */
+    public static void sanityCheckFailed(String title, boolean canContinue, String... message) {
+        final ExtendedDialog ed = new ExtendedDialog(mainFrame, title, tr("OK"), tr("Cancel"));
+        // Check if the dialog has not already been permanently hidden by user
+        if (!ed.toggleEnable("sanityCheckFailed").toggleCheckState() || !canContinue) {
+            final String content = Arrays.stream(message).collect(Collectors.joining("</li><li>",
+                    "<html><body><ul><li>", "</li></ul></body></html>"));
+            final JTextPane textField = new JTextPane();
+            textField.setContentType("text/html");
+            textField.setText(content);
+            TextContextualPopupMenu.enableMenuFor(textField, true);
+            ed.setButtonIcons("ok", "cancel").setCancelButton(2);
+            ed.setMinimumSize(new Dimension(480, 300));
+            ed.setIcon(JOptionPane.WARNING_MESSAGE);
+            ed.setContent(textField);
+            ed.showDialog();
+        }
+        if (!canContinue) {
+            Lifecycle.exitJosm(true, -1);
+        }
+    }
+
     /**
      * Called once at startup to initialize the main window content.
      * Should set {@link #menu} and {@link #mainPanel}
Index: core/src/org/openstreetmap/josm/gui/MainInitialization.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/core/src/org/openstreetmap/josm/gui/MainInitialization.java b/core/src/org/openstreetmap/josm/gui/MainInitialization.java
--- a/core/src/org/openstreetmap/josm/gui/MainInitialization.java	(revision 18917)
+++ b/core/src/org/openstreetmap/josm/gui/MainInitialization.java	(date 1703012450818)
@@ -71,7 +71,8 @@
             }),
             new InitializationTask(tr("Starting file watcher"), FileWatcher.getDefaultInstance()::start),
             new InitializationTask(tr("Executing platform startup hook"),
-                    () -> PlatformManager.getPlatform().startupHook(MainApplication::askUpdateJava, MainApplication::askMigrateWebStart)),
+                    () -> PlatformManager.getPlatform().startupHook(MainApplication::askUpdateJava,
+                            MainApplication::askMigrateWebStart, MainApplication::sanityCheckFailed)),
             new InitializationTask(tr("Building main menu"), application::initializeMainWindow),
             new InitializationTask(tr("Updating user interface"), () -> {
                 UndoRedoHandler.getInstance().addCommandQueueListener(application.redoUndoListener);
Index: core/src/org/openstreetmap/josm/tools/PlatformHook.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/core/src/org/openstreetmap/josm/tools/PlatformHook.java b/core/src/org/openstreetmap/josm/tools/PlatformHook.java
--- a/core/src/org/openstreetmap/josm/tools/PlatformHook.java	(revision 18917)
+++ b/core/src/org/openstreetmap/josm/tools/PlatformHook.java	(date 1703017943616)
@@ -1,6 +1,8 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.tools;
 
+import static org.openstreetmap.josm.tools.I18n.tr;
+
 import java.awt.GraphicsEnvironment;
 import java.awt.Toolkit;
 import java.awt.event.KeyEvent;
@@ -8,12 +10,14 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.lang.management.ManagementFactory;
 import java.nio.charset.StandardCharsets;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.text.DateFormat;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
@@ -86,10 +90,11 @@
       * OS, so we'll receive events from the system menu.
       * @param javaCallback Java expiration callback, providing GUI feedback
       * @param webStartCallback WebStart migration callback, providing GUI feedback
-      * @since 17679 (signature)
+      * @since xxx
       */
-    default void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback) {
-        // Do nothing
+    default void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback,
+            SanityCheckCallback sanityCheckCallback) {
+        startupSanityChecks(sanityCheckCallback);
     }
 
     /**
@@ -286,6 +291,20 @@
         void askMigrateWebStart(String url);
     }
 
+    /**
+     * Inform the user that a sanity check or checks failed
+     */
+    @FunctionalInterface
+    interface SanityCheckCallback {
+        /**
+         * Tells the user that a sanity check failed
+         * @param title The title of the message to show
+         * @param canContinue {@code true} if the failed sanity check(s) will not instantly kill JOSM when the user edits
+         * @param message The message parts to show the user (as a list)
+         */
+        void sanityCheckFailed(String title, boolean canContinue, String... message);
+    }
+
     /**
      * Checks if the running version of Java has expired, proposes to user to update it if needed.
      * @param callback Java expiration callback
@@ -369,6 +388,45 @@
         }
     }
 
+    default void startupSanityChecks(SanityCheckCallback sanityCheckCallback) {
+        final String arch = System.getProperty("os.arch");
+        final List<String> messages = new ArrayList<>();
+        final String jvmArch = System.getProperty("sun.arch.data.model");
+        boolean canContinue = true;
+        if (!"x86".equals(arch) && "32".equals(jvmArch)) {
+            messages.add(tr("Please use a 64 bit version of Java -- this will avoid out of memory errors"));
+        }
+        final String[] expectedJvmArguments = {
+                "--add-exports=java.base/sun.security.action=ALL-UNNAMED",
+                "--add-exports=java.desktop/com.sun.imageio.plugins.jpeg=ALL-UNNAMED",
+                "--add-exports=java.desktop/com.sun.imageio.spi=ALL-UNNAMED"
+
+        };
+        final List<String> vmArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
+        for (String arg : expectedJvmArguments) {
+            if (!vmArguments.contains(arg)) {
+                messages.add(tr("Missing JVM Arguments: ''{0}''", arg));
+            }
+        }
+        if (Utils.getJavaVersion() < 8) {
+            canContinue = false;
+            messages.add(tr("You must update Java to {0} in order to run this version of JOSM", 17));
+            // Reset webstart/java update prompts
+            Config.getPref().put("askUpdateWebStart", null);
+            Config.getPref().put("askUpdateJava" + Utils.getJavaLatestVersion(), null);
+            Config.getPref().put("askUpdateJavalatest", null);
+        }
+        if (!messages.isEmpty()) {
+            if (canContinue) {
+                sanityCheckCallback.sanityCheckFailed(tr("JOSM may work improperly"), true,
+                        messages.toArray(new String[0]));
+            } else {
+                sanityCheckCallback.sanityCheckFailed(tr("JOSM will be unable to work properly and will exit"), false,
+                        messages.toArray(new String[0]));
+            }
+        }
+    }
+
     /**
      * Called when interfacing with native OS functions. Currently only used with macOS.
      * The callback must perform all GUI-related tasks associated to an OS request.
Index: core/src/org/openstreetmap/josm/tools/PlatformHookOsx.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/core/src/org/openstreetmap/josm/tools/PlatformHookOsx.java b/core/src/org/openstreetmap/josm/tools/PlatformHookOsx.java
--- a/core/src/org/openstreetmap/josm/tools/PlatformHookOsx.java	(revision 18917)
+++ b/core/src/org/openstreetmap/josm/tools/PlatformHookOsx.java	(date 1703017830669)
@@ -68,7 +68,8 @@
     }
 
     @Override
-    public void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback) {
+    public void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback,
+            SanityCheckCallback sanityCheckCallback) {
         // Here we register callbacks for the menu entries in the system menu and file opening through double-click
         // https://openjdk.java.net/jeps/272
         // https://bugs.openjdk.java.net/browse/JDK-8048731
@@ -106,6 +107,7 @@
         warnSoonToBeUnsupportedJava(javaCallback);
         checkExpiredJava(javaCallback);
         checkWebStartMigration(webStartCallback);
+        PlatformHook.super.startupHook(javaCallback, webStartCallback, sanityCheckCallback);
     }
 
     @Override
Index: core/src/org/openstreetmap/josm/tools/PlatformHookUnixoid.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/core/src/org/openstreetmap/josm/tools/PlatformHookUnixoid.java b/core/src/org/openstreetmap/josm/tools/PlatformHookUnixoid.java
--- a/core/src/org/openstreetmap/josm/tools/PlatformHookUnixoid.java	(revision 18917)
+++ b/core/src/org/openstreetmap/josm/tools/PlatformHookUnixoid.java	(date 1703017844876)
@@ -63,8 +63,10 @@
     }
 
     @Override
-    public void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback) {
+    public void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback,
+            SanityCheckCallback sanityCheckCallback) {
         checkWebStartMigration(webStartCallback);
+        PlatformHook.super.startupHook(javaCallback, webStartCallback, sanityCheckCallback);
     }
 
     @Override
Index: core/src/org/openstreetmap/josm/tools/PlatformHookWindows.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/core/src/org/openstreetmap/josm/tools/PlatformHookWindows.java b/core/src/org/openstreetmap/josm/tools/PlatformHookWindows.java
--- a/core/src/org/openstreetmap/josm/tools/PlatformHookWindows.java	(revision 18917)
+++ b/core/src/org/openstreetmap/josm/tools/PlatformHookWindows.java	(date 1703017837065)
@@ -153,10 +153,12 @@
     }
 
     @Override
-    public void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback) {
+    public void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback,
+            SanityCheckCallback sanityCheckCallback) {
         warnSoonToBeUnsupportedJava(javaCallback);
         checkExpiredJava(javaCallback);
         checkWebStartMigration(webStartCallback);
+        PlatformHook.super.startupHook(javaCallback, webStartCallback, sanityCheckCallback);
     }
 
     @Override
Index: core/test/unit/org/openstreetmap/josm/tools/PlatformHookOsxTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/core/test/unit/org/openstreetmap/josm/tools/PlatformHookOsxTest.java b/core/test/unit/org/openstreetmap/josm/tools/PlatformHookOsxTest.java
--- a/core/test/unit/org/openstreetmap/josm/tools/PlatformHookOsxTest.java	(revision 18917)
+++ b/core/test/unit/org/openstreetmap/josm/tools/PlatformHookOsxTest.java	(date 1703010815486)
@@ -34,7 +34,8 @@
      */
     @Test
     void testStartupHook() {
-        hook.startupHook((a, b, c, d) -> System.out.println("java callback"), u -> System.out.println("webstart callback"));
+        hook.startupHook((a, b, c, d) -> System.out.println("java callback"), u -> System.out.println("webstart callback"),
+                (a, b, c) -> System.out.println("sanity check callback"));
     }
 
     /**
Index: core/test/unit/org/openstreetmap/josm/tools/PlatformHookWindowsTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/core/test/unit/org/openstreetmap/josm/tools/PlatformHookWindowsTest.java b/core/test/unit/org/openstreetmap/josm/tools/PlatformHookWindowsTest.java
--- a/core/test/unit/org/openstreetmap/josm/tools/PlatformHookWindowsTest.java	(revision 18917)
+++ b/core/test/unit/org/openstreetmap/josm/tools/PlatformHookWindowsTest.java	(date 1703012124778)
@@ -46,7 +46,8 @@
     void testStartupHook() {
         final PlatformHook.JavaExpirationCallback javaCallback = (a, b, c, d) -> System.out.println("java callback");
         final PlatformHook.WebStartMigrationCallback webstartCallback = u -> System.out.println("webstart callback");
-        assertDoesNotThrow(() -> hook.startupHook(javaCallback, webstartCallback));
+        final PlatformHook.SanityCheckCallback sanityCheckCallback = (a, b, c) -> System.out.println("sanity check callback");
+        assertDoesNotThrow(() -> hook.startupHook(javaCallback, webstartCallback, sanityCheckCallback));
     }
 
     /**
