Index: /trunk/src/org/openstreetmap/josm/Main.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/Main.java	(revision 14139)
+++ /trunk/src/org/openstreetmap/josm/Main.java	(revision 14140)
@@ -2,10 +2,6 @@
 package org.openstreetmap.josm;
 
-import static org.openstreetmap.josm.tools.I18n.tr;
-
 import java.awt.Component;
-import java.io.IOException;
 import java.net.URL;
-import java.nio.file.InvalidPathException;
 import java.util.Collection;
 import java.util.Collections;
@@ -15,7 +11,4 @@
 import org.openstreetmap.josm.data.Preferences;
 import org.openstreetmap.josm.data.UndoRedoHandler;
-import org.openstreetmap.josm.data.coor.conversion.CoordinateFormatManager;
-import org.openstreetmap.josm.data.coor.conversion.DecimalDegreesCoordinateFormat;
-import org.openstreetmap.josm.data.coor.conversion.ICoordinateFormat;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.IPrimitive;
@@ -29,8 +22,7 @@
 import org.openstreetmap.josm.io.NetworkManager;
 import org.openstreetmap.josm.io.OnlineResource;
+import org.openstreetmap.josm.spi.lifecycle.Lifecycle;
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.spi.preferences.IUrls;
-import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.PlatformHook;
 import org.openstreetmap.josm.tools.PlatformManager;
@@ -148,16 +140,4 @@
 
     /**
-     * Should be called before the main constructor to setup some parameter stuff
-     */
-    public static void preConstructorInit() {
-        // init default coordinate format
-        ICoordinateFormat fmt = CoordinateFormatManager.getCoordinateFormat(Config.getPref().get("coordinates"));
-        if (fmt == null) {
-            fmt = DecimalDegreesCoordinateFormat.INSTANCE;
-        }
-        CoordinateFormatManager.setCoordinateFormat(fmt);
-    }
-
-    /**
      * Closes JOSM and optionally terminates the Java Virtual Machine (JVM).
      * @param exit If {@code true}, the JVM is terminated by running {@link System#exit} with a given return code.
@@ -165,27 +145,9 @@
      * @return {@code true}
      * @since 12636
-     */
+     * @deprecated Use {@link Lifecycle#exitJosm}
+     */
+    @Deprecated
     public static boolean exitJosm(boolean exit, int exitCode) {
-        if (Main.main != null) {
-            Main.main.shutdown();
-        }
-
-        if (exit) {
-            System.exit(exitCode);
-        }
-        return true;
-    }
-
-    /**
-     * Shutdown JOSM.
-     */
-    protected void shutdown() {
-        ImageProvider.shutdown(false);
-        try {
-            pref.saveDefaults();
-        } catch (IOException | InvalidPathException ex) {
-            Logging.log(Logging.LEVEL_WARN, tr("Failed to save default preferences."), ex);
-        }
-        ImageProvider.shutdown(true);
+        return Lifecycle.exitJosm(exit, exitCode);
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 14139)
+++ /trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 14140)
@@ -87,5 +87,4 @@
 import org.openstreetmap.josm.data.UndoRedoHandler.CommandQueueListener;
 import org.openstreetmap.josm.data.Version;
-import org.openstreetmap.josm.data.cache.JCSCacheManager;
 import org.openstreetmap.josm.data.oauth.OAuthAccessTokenHolder;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -347,4 +346,5 @@
         getLayerManager().addLayerChangeListener(undoRedoCleaner);
         ProjectionRegistry.setboundsProvider(mainBoundsProvider);
+        Lifecycle.setShutdownSequence(new MainTermination(this));
     }
 
@@ -409,29 +409,11 @@
     }
 
-    @Override
-    protected void shutdown() {
-        try {
-            worker.shutdown();
-        } catch (SecurityException e) {
-            Logging.log(Logging.LEVEL_ERROR, "Unable to shutdown worker", e);
-        }
-        JCSCacheManager.shutdown();
-
-        if (mainFrame != null) {
-            mainFrame.storeState();
-        }
-        if (map != null) {
-            map.rememberToggleDialogWidth();
-        }
-        // Remove all layers because somebody may rely on layerRemoved events (like AutosaveTask)
-        layerManager.resetState();
-        super.shutdown();
-
-        try {
-            // in case the current task still hasn't finished
-            worker.shutdownNow();
-        } catch (SecurityException e) {
-            Logging.log(Logging.LEVEL_ERROR, "Unable to shutdown worker", e);
-        }
+    /**
+     * Returns the JOSM main frame.
+     * @return the JOSM main frame
+     * @since 14140
+     */
+    public final MainFrame getMainFrame() {
+        return mainFrame;
     }
 
@@ -566,5 +548,5 @@
                         reason != null ? reason : SaveLayersDialog.Reason.EXIT)));
         if (proceed) {
-            return Main.exitJosm(exit, exitCode);
+            return Lifecycle.exitJosm(exit, exitCode);
         }
         return false;
@@ -980,5 +962,4 @@
         setupNadGridSources();
         GuiHelper.translateJavaInternalMessages();
-        preConstructorInit();
 
         monitor.indeterminateSubTask(tr("Creating main GUI"));
Index: /trunk/src/org/openstreetmap/josm/gui/MainInitialization.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MainInitialization.java	(revision 14139)
+++ /trunk/src/org/openstreetmap/josm/gui/MainInitialization.java	(revision 14140)
@@ -14,4 +14,7 @@
 import org.openstreetmap.gui.jmapviewer.FeatureAdapter;
 import org.openstreetmap.josm.data.UndoRedoHandler;
+import org.openstreetmap.josm.data.coor.conversion.CoordinateFormatManager;
+import org.openstreetmap.josm.data.coor.conversion.DecimalDegreesCoordinateFormat;
+import org.openstreetmap.josm.data.coor.conversion.ICoordinateFormat;
 import org.openstreetmap.josm.data.validation.OsmValidator;
 import org.openstreetmap.josm.gui.layer.ImageryLayer;
@@ -28,4 +31,5 @@
 import org.openstreetmap.josm.spi.lifecycle.InitializationSequence;
 import org.openstreetmap.josm.spi.lifecycle.InitializationTask;
+import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.tools.I18n;
 import org.openstreetmap.josm.tools.Logging;
@@ -57,4 +61,11 @@
     public List<InitializationTask> beforeInitializationTasks() {
         return Arrays.asList(
+            new InitializationTask(tr("Initializing coordinate format"), () -> {
+                ICoordinateFormat fmt = CoordinateFormatManager.getCoordinateFormat(Config.getPref().get("coordinates"));
+                if (fmt == null) {
+                    fmt = DecimalDegreesCoordinateFormat.INSTANCE;
+                }
+                CoordinateFormatManager.setCoordinateFormat(fmt);
+            }),
             new InitializationTask(tr("Starting file watcher"), FileWatcher.getDefaultInstance()::start),
             new InitializationTask(tr("Executing platform startup hook"),
Index: /trunk/src/org/openstreetmap/josm/gui/MainTermination.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MainTermination.java	(revision 14140)
+++ /trunk/src/org/openstreetmap/josm/gui/MainTermination.java	(revision 14140)
@@ -0,0 +1,64 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+import java.nio.file.InvalidPathException;
+import java.util.Objects;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.cache.JCSCacheManager;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * JOSM termination sequence.
+ * @since 14140
+ */
+public class MainTermination implements Runnable {
+
+    private final MainApplication application;
+
+    /**
+     * Constructs a new {@code MainTermination}
+     * @param application Main application. Must not be null
+     */
+    public MainTermination(MainApplication application) {
+        this.application = Objects.requireNonNull(application);
+    }
+
+    @Override
+    public void run() {
+        try {
+            MainApplication.worker.shutdown();
+        } catch (SecurityException e) {
+            Logging.log(Logging.LEVEL_ERROR, "Unable to shutdown worker", e);
+        }
+        JCSCacheManager.shutdown();
+
+        if (application.getMainFrame() != null) {
+            application.getMainFrame().storeState();
+        }
+        if (MainApplication.getMap() != null) {
+            MainApplication.getMap().rememberToggleDialogWidth();
+        }
+        // Remove all layers because somebody may rely on layerRemoved events (like AutosaveTask)
+        MainApplication.getLayerManager().resetState();
+        ImageProvider.shutdown(false);
+        try {
+            Main.pref.saveDefaults();
+        } catch (IOException | InvalidPathException ex) {
+            Logging.log(Logging.LEVEL_WARN, tr("Failed to save default preferences."), ex);
+        }
+        ImageProvider.shutdown(true);
+
+        try {
+            // in case the current task still hasn't finished
+            MainApplication.worker.shutdownNow();
+        } catch (SecurityException e) {
+            Logging.log(Logging.LEVEL_ERROR, "Unable to shutdown worker", e);
+        }
+    }
+}
+
Index: /trunk/src/org/openstreetmap/josm/spi/lifecycle/Lifecycle.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/spi/lifecycle/Lifecycle.java	(revision 14139)
+++ /trunk/src/org/openstreetmap/josm/spi/lifecycle/Lifecycle.java	(revision 14140)
@@ -9,4 +9,5 @@
 import java.util.concurrent.Future;
 
+import org.openstreetmap.josm.tools.JosmRuntimeException;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Utils;
@@ -20,4 +21,6 @@
 
     private static volatile InitStatusListener initStatusListener;
+
+    private static volatile Runnable shutdownSequence;
 
     private Lifecycle() {
@@ -39,4 +42,22 @@
     public static void setInitStatusListener(InitStatusListener listener) {
         initStatusListener = Objects.requireNonNull(listener);
+    }
+
+    /**
+     * Gets shutdown sequence.
+     * @return shutdown sequence
+     * @since 14140
+     */
+    public static Runnable getShutdownSequence() {
+        return shutdownSequence;
+    }
+
+    /**
+     * Sets shutdown sequence.
+     * @param sequence shutdown sequence. Must not be null
+     * @since 14140
+     */
+    public static void setShutdownSequence(Runnable sequence) {
+        shutdownSequence = Objects.requireNonNull(sequence);
     }
 
@@ -66,5 +87,5 @@
             }
         } catch (InterruptedException | ExecutionException ex) {
-            throw new RuntimeException(ex);
+            throw new JosmRuntimeException(ex);
         }
 
@@ -77,5 +98,5 @@
             try {
                 task.call();
-            } catch (RuntimeException e) {
+            } catch (JosmRuntimeException e) {
                 // Can happen if the current projection needs NTV2 grid which is not available
                 // In this case we want the user be able to change his projection
@@ -84,3 +105,21 @@
         }
     }
+
+    /**
+     * Closes JOSM and optionally terminates the Java Virtual Machine (JVM).
+     * @param exit If {@code true}, the JVM is terminated by running {@link System#exit} with a given return code.
+     * @param exitCode The return code
+     * @return {@code true}
+     * @since 14140
+     */
+    public static boolean exitJosm(boolean exit, int exitCode) {
+        if (shutdownSequence != null) {
+            shutdownSequence.run();
+        }
+
+        if (exit) {
+            System.exit(exitCode);
+        }
+        return true;
+    }
 }
Index: unk/test/unit/org/openstreetmap/josm/MainTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/MainTest.java	(revision 14139)
+++ 	(revision )
@@ -1,33 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm;
-
-import static org.junit.Assert.assertNotNull;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.openstreetmap.josm.data.coor.conversion.CoordinateFormatManager;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-/**
- * Unit tests of {@link Main} class.
- */
-public class MainTest {
-
-    /**
-     * Setup test.
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().https().devAPI().main().projection();
-
-    /**
-     * Unit test of {@link Main#preConstructorInit}.
-     */
-    @Test
-    public void testPreConstructorInit() {
-        Main.preConstructorInit();
-        assertNotNull(CoordinateFormatManager.getDefaultFormat());
-    }
-}
