Rework the ThrustCurve motor loading process to be highly parallel. This significant...
[debian/openrocket] / core / src / net / sf / openrocket / startup / Startup2.java
1 package net.sf.openrocket.startup;
2
3 import java.awt.GraphicsEnvironment;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
6 import java.io.File;
7 import java.util.List;
8 import java.util.concurrent.Callable;
9 import java.util.concurrent.CountDownLatch;
10 import java.util.concurrent.ExecutorService;
11 import java.util.concurrent.Executors;
12 import java.util.concurrent.ThreadFactory;
13 import java.util.concurrent.atomic.AtomicInteger;
14
15 import javax.swing.SwingUtilities;
16 import javax.swing.Timer;
17 import javax.swing.ToolTipManager;
18
19 import net.sf.openrocket.communication.UpdateInfo;
20 import net.sf.openrocket.communication.UpdateInfoRetriever;
21 import net.sf.openrocket.database.ComponentPresetDatabase;
22 import net.sf.openrocket.database.Databases;
23 import net.sf.openrocket.database.ThrustCurveMotorSet;
24 import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
25 import net.sf.openrocket.file.iterator.DirectoryIterator;
26 import net.sf.openrocket.file.iterator.FileIterator;
27 import net.sf.openrocket.file.motor.MotorLoaderHelper;
28 import net.sf.openrocket.gui.dialogs.UpdateInfoDialog;
29 import net.sf.openrocket.gui.main.BasicFrame;
30 import net.sf.openrocket.gui.main.Splash;
31 import net.sf.openrocket.gui.main.SwingExceptionHandler;
32 import net.sf.openrocket.gui.util.GUIUtil;
33 import net.sf.openrocket.gui.util.SimpleFileFilter;
34 import net.sf.openrocket.gui.util.SwingPreferences;
35 import net.sf.openrocket.logging.LogHelper;
36 import net.sf.openrocket.motor.Motor;
37 import net.sf.openrocket.motor.ThrustCurveMotor;
38 import net.sf.openrocket.util.BuildProperties;
39
40 /**
41  * The second class in the OpenRocket startup sequence.  This class can assume the
42  * Application class to be properly set up, and can use any classes safely.
43  * 
44  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
45  */
46 public class Startup2 {
47         private static final LogHelper log = Application.getLogger();
48         
49
50         private static final String THRUSTCURVE_DIRECTORY = "datafiles/thrustcurves/";
51         
52         /**
53          * Run when starting up OpenRocket after Application has been set up.
54          * 
55          * @param args  command line arguments
56          */
57         static void runMain(final String[] args) throws Exception {
58                 
59                 log.info("Starting up OpenRocket version " + BuildProperties.getVersion());
60                 
61                 // Check that we're not running headless
62                 log.info("Checking for graphics head");
63                 checkHead();
64                 
65                 // Check that we're running a good version of a JRE
66                 log.info("Checking JRE compatibility");
67                 VersionHelper.checkVersion();
68                 VersionHelper.checkOpenJDK();
69                 
70                 // Run the actual startup method in the EDT since it can use progress dialogs etc.
71                 log.info("Moving startup to EDT");
72                 SwingUtilities.invokeAndWait(new Runnable() {
73                         @Override
74                         public void run() {
75                                 runInEDT(args);
76                         }
77                 });
78                 
79                 log.info("Startup complete");
80         }
81         
82         
83         /**
84          * Run in the EDT when starting up OpenRocket.
85          * 
86          * @param args  command line arguments
87          */
88         private static void runInEDT(String[] args) {
89                 
90                 // Initialize the splash screen with version info
91                 log.info("Initializing the splash screen");
92                 Splash.init();
93                 
94                 // Latch which counts the number of background loading processes we need to complete.
95                 CountDownLatch loading = new CountDownLatch(1);
96                 ExecutorService exec = Executors.newFixedThreadPool(1, new ThreadFactory() {
97
98                         @Override
99                         public Thread newThread(Runnable r) {
100                                 Thread t = new Thread(r);
101                                 t.setPriority(Thread.MIN_PRIORITY);
102                                 return t;
103                         }
104                         
105                 });
106
107                 // Must be done after localization is initialized
108                 ComponentPresetDatabase componentPresetDao = new ComponentPresetDatabase();
109                 exec.submit( new ComponentPresetLoader( loading, componentPresetDao));
110                 
111                 Application.setComponentPresetDao( componentPresetDao );
112                 
113                 // Setup the uncaught exception handler
114                 log.info("Registering exception handler");
115                 SwingExceptionHandler exceptionHandler = new SwingExceptionHandler();
116                 Application.setExceptionHandler(exceptionHandler);
117                 exceptionHandler.registerExceptionHandler();
118                 
119                 // Start update info fetching
120                 final UpdateInfoRetriever updateInfo;
121                 if ( Application.getPreferences().getCheckUpdates()) {
122                         log.info("Starting update check");
123                         updateInfo = new UpdateInfoRetriever();
124                         updateInfo.start();
125                 } else {
126                         log.info("Update check disabled");
127                         updateInfo = null;
128                 }
129                 
130                 // Set the best available look-and-feel
131                 log.info("Setting best LAF");
132                 GUIUtil.setBestLAF();
133                 
134                 // Set tooltip delay time.  Tooltips are used in MotorChooserDialog extensively.
135                 ToolTipManager.sharedInstance().setDismissDelay(30000);
136                 
137                 // Load defaults
138                 ((SwingPreferences) Application.getPreferences()).loadDefaultUnits();
139                 
140                 // Load motors etc.
141                 log.info("Loading databases");
142                 
143                 ConcurrentLoadingThrustCurveMotorSetDatabase motorLoader = new ConcurrentLoadingThrustCurveMotorSetDatabase(THRUSTCURVE_DIRECTORY);
144                 motorLoader.startLoading();
145                 Application.setMotorSetDatabase(motorLoader);
146
147                 Databases.fakeMethod();
148                 
149                 try {
150                         loading.await();
151                 } catch ( InterruptedException iex) {
152                         
153                 }
154
155                 // Starting action (load files or open new document)
156                 log.info("Opening main application window");
157                 if (!handleCommandLine(args)) {
158                         BasicFrame.newAction();
159                 }
160                 
161                 // Check whether update info has been fetched or whether it needs more time
162                 log.info("Checking update status");
163                 checkUpdateStatus(updateInfo);
164                 
165         }
166         
167         
168         /**
169          * Check that the JRE is not running headless.
170          */
171         private static void checkHead() {
172                 
173                 if (GraphicsEnvironment.isHeadless()) {
174                         log.error("Application is headless.");
175                         System.err.println();
176                         System.err.println("OpenRocket cannot currently be run without the graphical " +
177                                         "user interface.");
178                         System.err.println();
179                         System.exit(1);
180                 }
181                 
182         }
183         
184         
185         private static void checkUpdateStatus(final UpdateInfoRetriever updateInfo) {
186                 if (updateInfo == null)
187                         return;
188                 
189                 int delay = 1000;
190                 if (!updateInfo.isRunning())
191                         delay = 100;
192                 
193                 final Timer timer = new Timer(delay, null);
194                 
195                 ActionListener listener = new ActionListener() {
196                         private int count = 5;
197                         
198                         @Override
199                         public void actionPerformed(ActionEvent e) {
200                                 if (!updateInfo.isRunning()) {
201                                         timer.stop();
202                                         
203                                         String current = BuildProperties.getVersion();
204                                         String last = Application.getPreferences().getString(Preferences.LAST_UPDATE, "");
205                                         
206                                         UpdateInfo info = updateInfo.getUpdateInfo();
207                                         if (info != null && info.getLatestVersion() != null &&
208                                                         !current.equals(info.getLatestVersion()) &&
209                                                         !last.equals(info.getLatestVersion())) {
210                                                 
211                                                 UpdateInfoDialog infoDialog = new UpdateInfoDialog(info);
212                                                 infoDialog.setVisible(true);
213                                                 if (infoDialog.isReminderSelected()) {
214                                                         Application.getPreferences().putString(Preferences.LAST_UPDATE, "");
215                                                 } else {
216                                                         Application.getPreferences().putString(Preferences.LAST_UPDATE, info.getLatestVersion());
217                                                 }
218                                         }
219                                 }
220                                 count--;
221                                 if (count <= 0)
222                                         timer.stop();
223                         }
224                 };
225                 timer.addActionListener(listener);
226                 timer.start();
227         }
228         
229         private static class ComponentPresetLoader implements Callable {
230
231                 CountDownLatch latch;
232                 ComponentPresetDatabase componentPresetDao;
233                 
234                 private ComponentPresetLoader( CountDownLatch latch, ComponentPresetDatabase componentPresetDao ) {
235                         this.componentPresetDao = componentPresetDao;
236                         this.latch = latch;
237                 }
238                 
239                 @Override
240                 public Object call() throws Exception {
241                         long start = System.currentTimeMillis();
242                         componentPresetDao.load("datafiles/presets", "(?i).*orc");
243                         latch.countDown();
244                         long end = System.currentTimeMillis();
245                         log.debug("Time to load presets: " + (end-start) + "ms");
246                         return null;
247                 }
248
249         }
250         
251         /**
252          * Handles arguments passed from the command line.  This may be used either
253          * when starting the first instance of OpenRocket or later when OpenRocket is
254          * executed again while running.
255          * 
256          * @param args  the command-line arguments.
257          * @return              whether a new frame was opened or similar user desired action was
258          *                              performed as a result.
259          */
260         private static boolean handleCommandLine(String[] args) {
261                 
262                 // Check command-line for files
263                 boolean opened = false;
264                 for (String file : args) {
265                         if (BasicFrame.open(new File(file), null)) {
266                                 opened = true;
267                         }
268                 }
269                 return opened;
270         }
271         
272 }