Index: trunk/src/org/openstreetmap/josm/Main.java
===================================================================
--- trunk/src/org/openstreetmap/josm/Main.java	(revision 14138)
+++ trunk/src/org/openstreetmap/josm/Main.java	(revision 14139)
@@ -10,12 +10,6 @@
 import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
 
 import org.openstreetmap.josm.data.Preferences;
@@ -35,14 +29,10 @@
 import org.openstreetmap.josm.io.NetworkManager;
 import org.openstreetmap.josm.io.OnlineResource;
-import org.openstreetmap.josm.spi.lifecycle.InitializationTask;
 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.JosmRuntimeException;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.PlatformHook;
 import org.openstreetmap.josm.tools.PlatformManager;
-import org.openstreetmap.josm.tools.Utils;
-import org.openstreetmap.josm.tools.bugreport.BugReport;
 
 /**
@@ -93,5 +83,4 @@
     /**
      * Constructs new {@code Main} object.
-     * @see #initialize()
      */
     protected Main() {
@@ -101,91 +90,4 @@
     private static void setInstance(Main instance) {
         main = instance;
-    }
-
-    /**
-     * Initializes the main object. A lot of global variables are initialized here.
-     * @since 10340
-     */
-    public void initialize() {
-        // Initializes tasks that must be run before parallel tasks
-        runInitializationTasks(beforeInitializationTasks());
-
-        // Initializes tasks to be executed (in parallel) by a ExecutorService
-        try {
-            ExecutorService service = Executors.newFixedThreadPool(
-                    Runtime.getRuntime().availableProcessors(), Utils.newThreadFactory("main-init-%d", Thread.NORM_PRIORITY));
-            for (Future<Void> i : service.invokeAll(parallelInitializationTasks())) {
-                i.get();
-            }
-            // asynchronous initializations to be completed eventually
-            asynchronousRunnableTasks().forEach(service::submit);
-            asynchronousCallableTasks().forEach(service::submit);
-            try {
-                service.shutdown();
-            } catch (SecurityException e) {
-                Logging.log(Logging.LEVEL_ERROR, "Unable to shutdown executor service", e);
-            }
-        } catch (InterruptedException | ExecutionException ex) {
-            throw new JosmRuntimeException(ex);
-        }
-
-        // Initializes tasks that must be run after parallel tasks
-        runInitializationTasks(afterInitializationTasks());
-    }
-
-    private static void runInitializationTasks(List<InitializationTask> tasks) {
-        for (InitializationTask task : tasks) {
-            try {
-                task.call();
-            } 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
-                BugReport.intercept(e).warn();
-            }
-        }
-    }
-
-    /**
-     * Returns tasks that must be run before parallel tasks.
-     * @return tasks that must be run before parallel tasks
-     * @see #afterInitializationTasks
-     * @see #parallelInitializationTasks
-     */
-    protected List<InitializationTask> beforeInitializationTasks() {
-        return Collections.emptyList();
-    }
-
-    /**
-     * Returns tasks to be executed (in parallel) by a ExecutorService.
-     * @return tasks to be executed (in parallel) by a ExecutorService
-     */
-    protected Collection<InitializationTask> parallelInitializationTasks() {
-        return Collections.emptyList();
-    }
-
-    /**
-     * Returns asynchronous callable initializations to be completed eventually
-     * @return asynchronous callable initializations to be completed eventually
-     */
-    protected List<Callable<?>> asynchronousCallableTasks() {
-        return Collections.emptyList();
-    }
-
-    /**
-     * Returns asynchronous runnable initializations to be completed eventually
-     * @return asynchronous runnable initializations to be completed eventually
-     */
-    protected List<Runnable> asynchronousRunnableTasks() {
-        return Collections.emptyList();
-    }
-
-    /**
-     * Returns tasks that must be run after parallel tasks.
-     * @return tasks that must be run after parallel tasks
-     * @see #beforeInitializationTasks
-     * @see #parallelInitializationTasks
-     */
-    protected List<InitializationTask> afterInitializationTasks() {
-        return Collections.emptyList();
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 14138)
+++ trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 14139)
@@ -6,5 +6,4 @@
 import static org.openstreetmap.josm.tools.Utils.getSystemProperty;
 
-import java.awt.BorderLayout;
 import java.awt.Container;
 import java.awt.Dimension;
@@ -13,5 +12,4 @@
 import java.awt.GridBagLayout;
 import java.awt.Toolkit;
-import java.awt.event.KeyEvent;
 import java.io.File;
 import java.io.IOException;
@@ -46,5 +44,4 @@
 import java.util.Set;
 import java.util.TreeSet;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -69,5 +66,4 @@
 
 import org.jdesktop.swinghelper.debug.CheckThreadViolationRepaintManager;
-import org.openstreetmap.gui.jmapviewer.FeatureAdapter;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.DeleteAction;
@@ -108,5 +104,4 @@
 import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileWrapper;
 import org.openstreetmap.josm.data.projection.datum.NTV2Proj4DirGridShiftFileSource;
-import org.openstreetmap.josm.data.validation.OsmValidator;
 import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
 import org.openstreetmap.josm.gui.ProgramArguments.Option;
@@ -118,5 +113,4 @@
 import org.openstreetmap.josm.gui.io.SaveLayersDialog;
 import org.openstreetmap.josm.gui.layer.AutosaveTask;
-import org.openstreetmap.josm.gui.layer.ImageryLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
@@ -126,5 +120,4 @@
 import org.openstreetmap.josm.gui.layer.MainLayerManager;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.gui.layer.TMSLayer;
 import org.openstreetmap.josm.gui.mappaint.RenderingCLI;
 import org.openstreetmap.josm.gui.mappaint.loader.MapPaintStyleLoader;
@@ -132,10 +125,7 @@
 import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
 import org.openstreetmap.josm.gui.preferences.display.LafPreference;
-import org.openstreetmap.josm.gui.preferences.imagery.ImageryPreference;
-import org.openstreetmap.josm.gui.preferences.map.MapPaintPreference;
 import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
 import org.openstreetmap.josm.gui.preferences.server.ProxyPreference;
 import org.openstreetmap.josm.gui.progress.swing.ProgressMonitorExecutor;
-import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.util.RedirectInputMap;
@@ -149,8 +139,5 @@
 import org.openstreetmap.josm.io.NetworkManager;
 import org.openstreetmap.josm.io.OnlineResource;
-import org.openstreetmap.josm.io.OsmApi;
-import org.openstreetmap.josm.io.OsmApiInitializationException;
 import org.openstreetmap.josm.io.OsmConnection;
-import org.openstreetmap.josm.io.OsmTransferCanceledException;
 import org.openstreetmap.josm.io.OsmTransferException;
 import org.openstreetmap.josm.io.auth.AbstractCredentialsAgent;
@@ -162,5 +149,4 @@
 import org.openstreetmap.josm.plugins.PluginInformation;
 import org.openstreetmap.josm.spi.lifecycle.InitStatusListener;
-import org.openstreetmap.josm.spi.lifecycle.InitializationTask;
 import org.openstreetmap.josm.spi.lifecycle.Lifecycle;
 import org.openstreetmap.josm.spi.preferences.Config;
@@ -173,13 +159,9 @@
 import org.openstreetmap.josm.tools.JosmRuntimeException;
 import org.openstreetmap.josm.tools.Logging;
-import org.openstreetmap.josm.tools.OpenBrowser;
 import org.openstreetmap.josm.tools.OsmUrlToBounds;
-import org.openstreetmap.josm.tools.OverpassTurboQueryWizard;
 import org.openstreetmap.josm.tools.PlatformHook.NativeOsCallback;
 import org.openstreetmap.josm.tools.PlatformHookWindows;
 import org.openstreetmap.josm.tools.PlatformManager;
-import org.openstreetmap.josm.tools.RightAndLefthandTraffic;
 import org.openstreetmap.josm.tools.Shortcut;
-import org.openstreetmap.josm.tools.Territories;
 import org.openstreetmap.josm.tools.Utils;
 import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
@@ -312,5 +294,5 @@
      * Listener that sets the enabled state of undo/redo menu entries.
      */
-    private final CommandQueueListener redoUndoListener = (queueSize, redoSize) -> {
+    final CommandQueueListener redoUndoListener = (queueSize, redoSize) -> {
             menu.undo.setEnabled(queueSize > 0);
             menu.redo.setEnabled(redoSize > 0);
@@ -407,89 +389,4 @@
             }
         }
-    }
-
-    @Override
-    protected List<InitializationTask> beforeInitializationTasks() {
-        return Arrays.asList(
-            new InitializationTask(tr("Starting file watcher"), FileWatcher.getDefaultInstance()::start),
-            new InitializationTask(tr("Executing platform startup hook"),
-                    () -> PlatformManager.getPlatform().startupHook(MainApplication::askUpdateJava)),
-            new InitializationTask(tr("Building main menu"), this::initializeMainWindow),
-            new InitializationTask(tr("Updating user interface"), () -> {
-                UndoRedoHandler.getInstance().addCommandQueueListener(redoUndoListener);
-                // creating toolbar
-                GuiHelper.runInEDTAndWait(() -> contentPanePrivate.add(toolbar.control, BorderLayout.NORTH));
-                // help shortcut
-                registerActionShortcut(menu.help, Shortcut.registerShortcut("system:help", tr("Help"),
-                        KeyEvent.VK_F1, Shortcut.DIRECT));
-            }),
-            // This needs to be done before RightAndLefthandTraffic::initialize is called
-            new InitializationTask(tr("Initializing internal boundaries data"), Territories::initialize)
-        );
-    }
-
-    @Override
-    protected Collection<InitializationTask> parallelInitializationTasks() {
-        return Arrays.asList(
-            new InitializationTask(tr("Initializing OSM API"), () -> {
-                    OsmApi.addOsmApiInitializationListener(api -> {
-                        // This checks if there are any layers currently displayed that are now on the blacklist, and removes them.
-                        // This is a rare situation - probably only occurs if the user changes the API URL in the preferences menu.
-                        // Otherwise they would not have been able to load the layers in the first place because they would have been disabled
-                        if (isDisplayingMapView()) {
-                            for (Layer l : getLayerManager().getLayersOfType(ImageryLayer.class)) {
-                                if (((ImageryLayer) l).getInfo().isBlacklisted()) {
-                                    Logging.info(tr("Removed layer {0} because it is not allowed by the configured API.", l.getName()));
-                                    getLayerManager().removeLayer(l);
-                                }
-                            }
-                        }
-                    });
-                    // We try to establish an API connection early, so that any API
-                    // capabilities are already known to the editor instance. However
-                    // if it goes wrong that's not critical at this stage.
-                    try {
-                        OsmApi.getOsmApi().initialize(null, true);
-                    } catch (OsmTransferCanceledException | OsmApiInitializationException | SecurityException e) {
-                        Logging.warn(Logging.getErrorMessage(Utils.getRootCause(e)));
-                    }
-                }),
-            new InitializationTask(tr("Initializing internal traffic data"), RightAndLefthandTraffic::initialize),
-            new InitializationTask(tr("Initializing validator"), OsmValidator::initialize),
-            new InitializationTask(tr("Initializing presets"), TaggingPresets::initialize),
-            new InitializationTask(tr("Initializing map styles"), MapPaintPreference::initialize),
-            new InitializationTask(tr("Loading imagery preferences"), ImageryPreference::initialize)
-        );
-    }
-
-    @Override
-    protected List<Callable<?>> asynchronousCallableTasks() {
-        return Arrays.asList(
-                OverpassTurboQueryWizard::getInstance
-            );
-    }
-
-    @Override
-    protected List<Runnable> asynchronousRunnableTasks() {
-        return Arrays.asList(
-                TMSLayer::getCache,
-                OsmValidator::initializeTests
-            );
-    }
-
-    @Override
-    protected List<InitializationTask> afterInitializationTasks() {
-        return Arrays.asList(
-            new InitializationTask(tr("Updating user interface"), () -> GuiHelper.runInEDTAndWait(() -> {
-                // hooks for the jmapviewer component
-                FeatureAdapter.registerBrowserAdapter(OpenBrowser::displayUrl);
-                FeatureAdapter.registerTranslationAdapter(I18n::tr);
-                FeatureAdapter.registerLoggingAdapter(name -> Logging.getLogger());
-                // UI update
-                toolbar.refreshToolbarControl();
-                toolbar.control.updateUI();
-                contentPanePrivate.updateUI();
-            }))
-        );
     }
 
@@ -1086,6 +983,5 @@
 
         monitor.indeterminateSubTask(tr("Creating main GUI"));
-        final Main main = new MainApplication(mainFrame);
-        main.initialize();
+        Lifecycle.initialize(new MainInitialization(new MainApplication(mainFrame)));
 
         if (!skipLoadingPlugins) {
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 14139)
@@ -0,0 +1,140 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.event.KeyEvent;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Callable;
+
+import org.openstreetmap.gui.jmapviewer.FeatureAdapter;
+import org.openstreetmap.josm.data.UndoRedoHandler;
+import org.openstreetmap.josm.data.validation.OsmValidator;
+import org.openstreetmap.josm.gui.layer.ImageryLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.TMSLayer;
+import org.openstreetmap.josm.gui.preferences.imagery.ImageryPreference;
+import org.openstreetmap.josm.gui.preferences.map.MapPaintPreference;
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.io.FileWatcher;
+import org.openstreetmap.josm.io.OsmApi;
+import org.openstreetmap.josm.io.OsmApiInitializationException;
+import org.openstreetmap.josm.io.OsmTransferCanceledException;
+import org.openstreetmap.josm.spi.lifecycle.InitializationSequence;
+import org.openstreetmap.josm.spi.lifecycle.InitializationTask;
+import org.openstreetmap.josm.tools.I18n;
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.OpenBrowser;
+import org.openstreetmap.josm.tools.OverpassTurboQueryWizard;
+import org.openstreetmap.josm.tools.PlatformManager;
+import org.openstreetmap.josm.tools.RightAndLefthandTraffic;
+import org.openstreetmap.josm.tools.Shortcut;
+import org.openstreetmap.josm.tools.Territories;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * JOSM initialization sequence.
+ * @since 14139
+ */
+public class MainInitialization implements InitializationSequence {
+
+    private final MainApplication application;
+
+    /**
+     * Constructs a new {@code MainInitialization}
+     * @param application Main application. Must not be null
+     */
+    public MainInitialization(MainApplication application) {
+        this.application = Objects.requireNonNull(application);
+    }
+
+    @Override
+    public List<InitializationTask> beforeInitializationTasks() {
+        return Arrays.asList(
+            new InitializationTask(tr("Starting file watcher"), FileWatcher.getDefaultInstance()::start),
+            new InitializationTask(tr("Executing platform startup hook"),
+                    () -> PlatformManager.getPlatform().startupHook(MainApplication::askUpdateJava)),
+            new InitializationTask(tr("Building main menu"), application::initializeMainWindow),
+            new InitializationTask(tr("Updating user interface"), () -> {
+                UndoRedoHandler.getInstance().addCommandQueueListener(application.redoUndoListener);
+                // creating toolbar
+                GuiHelper.runInEDTAndWait(() -> MainApplication.contentPanePrivate.add(MainApplication.toolbar.control, BorderLayout.NORTH));
+                // help shortcut
+                MainApplication.registerActionShortcut(MainApplication.menu.help,
+                        Shortcut.registerShortcut("system:help", tr("Help"), KeyEvent.VK_F1, Shortcut.DIRECT));
+            }),
+            // This needs to be done before RightAndLefthandTraffic::initialize is called
+            new InitializationTask(tr("Initializing internal boundaries data"), Territories::initialize)
+        );
+    }
+
+    @Override
+    public Collection<InitializationTask> parallelInitializationTasks() {
+        return Arrays.asList(
+            new InitializationTask(tr("Initializing OSM API"), () -> {
+                    OsmApi.addOsmApiInitializationListener(api -> {
+                        // This checks if there are any layers currently displayed that are now on the blacklist, and removes them.
+                        // This is a rare situation - probably only occurs if the user changes the API URL in the preferences menu.
+                        // Otherwise they would not have been able to load the layers in the first place because they would have been disabled
+                        if (MainApplication.isDisplayingMapView()) {
+                            for (Layer l : MainApplication.getLayerManager().getLayersOfType(ImageryLayer.class)) {
+                                if (((ImageryLayer) l).getInfo().isBlacklisted()) {
+                                    Logging.info(tr("Removed layer {0} because it is not allowed by the configured API.", l.getName()));
+                                    MainApplication.getLayerManager().removeLayer(l);
+                                }
+                            }
+                        }
+                    });
+                    // We try to establish an API connection early, so that any API
+                    // capabilities are already known to the editor instance. However
+                    // if it goes wrong that's not critical at this stage.
+                    try {
+                        OsmApi.getOsmApi().initialize(null, true);
+                    } catch (OsmTransferCanceledException | OsmApiInitializationException | SecurityException e) {
+                        Logging.warn(Logging.getErrorMessage(Utils.getRootCause(e)));
+                    }
+                }),
+            new InitializationTask(tr("Initializing internal traffic data"), RightAndLefthandTraffic::initialize),
+            new InitializationTask(tr("Initializing validator"), OsmValidator::initialize),
+            new InitializationTask(tr("Initializing presets"), TaggingPresets::initialize),
+            new InitializationTask(tr("Initializing map styles"), MapPaintPreference::initialize),
+            new InitializationTask(tr("Loading imagery preferences"), ImageryPreference::initialize)
+        );
+    }
+
+    @Override
+    public List<Callable<?>> asynchronousCallableTasks() {
+        return Arrays.asList(
+                OverpassTurboQueryWizard::getInstance
+            );
+    }
+
+    @Override
+    public List<Runnable> asynchronousRunnableTasks() {
+        return Arrays.asList(
+                TMSLayer::getCache,
+                OsmValidator::initializeTests
+            );
+    }
+
+    @Override
+    public List<InitializationTask> afterInitializationTasks() {
+        return Arrays.asList(
+            new InitializationTask(tr("Updating user interface"), () -> GuiHelper.runInEDTAndWait(() -> {
+                // hooks for the jmapviewer component
+                FeatureAdapter.registerBrowserAdapter(OpenBrowser::displayUrl);
+                FeatureAdapter.registerTranslationAdapter(I18n::tr);
+                FeatureAdapter.registerLoggingAdapter(name -> Logging.getLogger());
+                // UI update
+                MainApplication.toolbar.refreshToolbarControl();
+                MainApplication.toolbar.control.updateUI();
+                MainApplication.contentPanePrivate.updateUI();
+            }))
+        );
+    }
+}
Index: trunk/src/org/openstreetmap/josm/spi/lifecycle/InitializationSequence.java
===================================================================
--- trunk/src/org/openstreetmap/josm/spi/lifecycle/InitializationSequence.java	(revision 14139)
+++ trunk/src/org/openstreetmap/josm/spi/lifecycle/InitializationSequence.java	(revision 14139)
@@ -0,0 +1,58 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.spi.lifecycle;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Defines the initialization sequence.
+ * @since 14139
+ */
+public interface InitializationSequence {
+
+    /**
+     * Returns tasks that must be run before parallel tasks.
+     * @return tasks that must be run before parallel tasks
+     * @see #afterInitializationTasks
+     * @see #parallelInitializationTasks
+     */
+    default List<InitializationTask> beforeInitializationTasks() {
+        return Collections.emptyList();
+    }
+
+    /**
+     * Returns tasks to be executed (in parallel) by a ExecutorService.
+     * @return tasks to be executed (in parallel) by a ExecutorService
+     */
+    default Collection<InitializationTask> parallelInitializationTasks() {
+        return Collections.emptyList();
+    }
+
+    /**
+     * Returns asynchronous callable initializations to be completed eventually
+     * @return asynchronous callable initializations to be completed eventually
+     */
+    default List<Callable<?>> asynchronousCallableTasks() {
+        return Collections.emptyList();
+    }
+
+    /**
+     * Returns asynchronous runnable initializations to be completed eventually
+     * @return asynchronous runnable initializations to be completed eventually
+     */
+    default List<Runnable> asynchronousRunnableTasks() {
+        return Collections.emptyList();
+    }
+
+    /**
+     * Returns tasks that must be run after parallel tasks.
+     * @return tasks that must be run after parallel tasks
+     * @see #beforeInitializationTasks
+     * @see #parallelInitializationTasks
+     */
+    default List<InitializationTask> afterInitializationTasks() {
+        return Collections.emptyList();
+    }
+}
Index: trunk/src/org/openstreetmap/josm/spi/lifecycle/Lifecycle.java
===================================================================
--- trunk/src/org/openstreetmap/josm/spi/lifecycle/Lifecycle.java	(revision 14138)
+++ trunk/src/org/openstreetmap/josm/spi/lifecycle/Lifecycle.java	(revision 14139)
@@ -2,5 +2,14 @@
 package org.openstreetmap.josm.spi.lifecycle;
 
+import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.bugreport.BugReport;
 
 /**
@@ -31,3 +40,47 @@
         initStatusListener = Objects.requireNonNull(listener);
     }
+
+    /**
+     * Initializes the main object. A lot of global variables are initialized here.
+     * @param initSequence Initialization sequence
+     * @since 14139
+     */
+    public static void initialize(InitializationSequence initSequence) {
+        // Initializes tasks that must be run before parallel tasks
+        runInitializationTasks(initSequence.beforeInitializationTasks());
+
+        // Initializes tasks to be executed (in parallel) by a ExecutorService
+        try {
+            ExecutorService service = Executors.newFixedThreadPool(
+                    Runtime.getRuntime().availableProcessors(), Utils.newThreadFactory("main-init-%d", Thread.NORM_PRIORITY));
+            for (Future<Void> i : service.invokeAll(initSequence.parallelInitializationTasks())) {
+                i.get();
+            }
+            // asynchronous initializations to be completed eventually
+            initSequence.asynchronousRunnableTasks().forEach(service::submit);
+            initSequence.asynchronousCallableTasks().forEach(service::submit);
+            try {
+                service.shutdown();
+            } catch (SecurityException e) {
+                Logging.log(Logging.LEVEL_ERROR, "Unable to shutdown executor service", e);
+            }
+        } catch (InterruptedException | ExecutionException ex) {
+            throw new RuntimeException(ex);
+        }
+
+        // Initializes tasks that must be run after parallel tasks
+        runInitializationTasks(initSequence.afterInitializationTasks());
+    }
+
+    private static void runInitializationTasks(List<InitializationTask> tasks) {
+        for (InitializationTask task : tasks) {
+            try {
+                task.call();
+            } catch (RuntimeException 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
+                BugReport.intercept(e).warn();
+            }
+        }
+    }
 }
