001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.spi.lifecycle; 003 004import java.util.List; 005import java.util.Objects; 006import java.util.concurrent.ExecutionException; 007import java.util.concurrent.ExecutorService; 008import java.util.concurrent.Executors; 009import java.util.concurrent.Future; 010 011import org.openstreetmap.josm.tools.JosmRuntimeException; 012import org.openstreetmap.josm.tools.Logging; 013import org.openstreetmap.josm.tools.Utils; 014import org.openstreetmap.josm.tools.bugreport.BugReport; 015 016/** 017 * JOSM lifecycle. 018 * @since 14125 019 */ 020public final class Lifecycle { 021 022 private static volatile InitStatusListener initStatusListener; 023 024 private static volatile Runnable shutdownSequence; 025 026 private Lifecycle() { 027 // Hide constructor 028 } 029 030 /** 031 * Gets initialization task listener. 032 * @return initialization task listener 033 */ 034 public static InitStatusListener getInitStatusListener() { 035 return initStatusListener; 036 } 037 038 /** 039 * Sets initialization task listener. 040 * @param listener initialization task listener. Must not be null 041 */ 042 public static void setInitStatusListener(InitStatusListener listener) { 043 initStatusListener = Objects.requireNonNull(listener); 044 } 045 046 /** 047 * Gets shutdown sequence. 048 * @return shutdown sequence 049 * @since 14140 050 */ 051 public static Runnable getShutdownSequence() { 052 return shutdownSequence; 053 } 054 055 /** 056 * Sets shutdown sequence. 057 * @param sequence shutdown sequence. Must not be null 058 * @since 14140 059 */ 060 public static void setShutdownSequence(Runnable sequence) { 061 shutdownSequence = Objects.requireNonNull(sequence); 062 } 063 064 /** 065 * Initializes the main object. A lot of global variables are initialized here. 066 * @param initSequence Initialization sequence 067 * @since 14139 068 */ 069 public static void initialize(InitializationSequence initSequence) { 070 // Initializes tasks that must be run before parallel tasks 071 runInitializationTasks(initSequence.beforeInitializationTasks()); 072 073 // Initializes tasks to be executed (in parallel) by a ExecutorService 074 try { 075 ExecutorService service = Executors.newFixedThreadPool( 076 Runtime.getRuntime().availableProcessors(), Utils.newThreadFactory("main-init-%d", Thread.NORM_PRIORITY)); 077 for (Future<Void> i : service.invokeAll(initSequence.parallelInitializationTasks())) { 078 i.get(); 079 } 080 // asynchronous initializations to be completed eventually 081 initSequence.asynchronousRunnableTasks().forEach(service::submit); 082 initSequence.asynchronousCallableTasks().forEach(service::submit); 083 try { 084 service.shutdown(); 085 } catch (SecurityException e) { 086 Logging.log(Logging.LEVEL_ERROR, "Unable to shutdown executor service", e); 087 } 088 } catch (InterruptedException | ExecutionException ex) { 089 throw new JosmRuntimeException(ex); 090 } 091 092 // Initializes tasks that must be run after parallel tasks 093 runInitializationTasks(initSequence.afterInitializationTasks()); 094 } 095 096 private static void runInitializationTasks(List<InitializationTask> tasks) { 097 for (InitializationTask task : tasks) { 098 try { 099 task.call(); 100 } catch (JosmRuntimeException e) { 101 // Can happen if the current projection needs NTV2 grid which is not available 102 // In this case we want the user be able to change his projection 103 BugReport.intercept(e).warn(); 104 } 105 } 106 } 107 108 /** 109 * Closes JOSM and optionally terminates the Java Virtual Machine (JVM). 110 * @param exit If {@code true}, the JVM is terminated by running {@link System#exit} with a given return code. 111 * @param exitCode The return code 112 * @return {@code true} 113 * @since 14140 114 */ 115 public static boolean exitJosm(boolean exit, int exitCode) { 116 if (shutdownSequence != null) { 117 shutdownSequence.run(); 118 } 119 120 if (exit) { 121 System.exit(exitCode); 122 } 123 return true; 124 } 125}