1 package net.sf.openrocket.startup;
3 import java.awt.GraphicsEnvironment;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
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;
15 import javax.swing.SwingUtilities;
16 import javax.swing.Timer;
17 import javax.swing.ToolTipManager;
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;
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.
44 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
46 public class Startup2 {
47 private static final LogHelper log = Application.getLogger();
50 private static final String THRUSTCURVE_DIRECTORY = "datafiles/thrustcurves/";
52 /** Block motor loading for this many milliseconds */
53 private static AtomicInteger blockLoading = new AtomicInteger(Integer.MAX_VALUE);
58 * Run when starting up OpenRocket after Application has been set up.
60 * @param args command line arguments
62 static void runMain(final String[] args) throws Exception {
64 log.info("Starting up OpenRocket version " + BuildProperties.getVersion());
66 // Check that we're not running headless
67 log.info("Checking for graphics head");
70 // Check that we're running a good version of a JRE
71 log.info("Checking JRE compatibility");
72 VersionHelper.checkVersion();
73 VersionHelper.checkOpenJDK();
75 // Run the actual startup method in the EDT since it can use progress dialogs etc.
76 log.info("Moving startup to EDT");
77 SwingUtilities.invokeAndWait(new Runnable() {
84 log.info("Startup complete");
89 * Run in the EDT when starting up OpenRocket.
91 * @param args command line arguments
93 private static void runInEDT(String[] args) {
95 // Initialize the splash screen with version info
96 log.info("Initializing the splash screen");
99 // Latch which counts the number of background loading processes we need to complete.
100 CountDownLatch loading = new CountDownLatch(1);
101 ExecutorService exec = Executors.newFixedThreadPool(2, new ThreadFactory() {
104 public Thread newThread(Runnable r) {
105 Thread t = new Thread(r);
106 t.setPriority(Thread.MIN_PRIORITY);
112 // Must be done after localization is initialized
113 ComponentPresetDatabase componentPresetDao = new ComponentPresetDatabase();
114 exec.submit( new ComponentPresetLoader( loading, componentPresetDao));
116 Application.setComponentPresetDao( componentPresetDao );
118 // Setup the uncaught exception handler
119 log.info("Registering exception handler");
120 SwingExceptionHandler exceptionHandler = new SwingExceptionHandler();
121 Application.setExceptionHandler(exceptionHandler);
122 exceptionHandler.registerExceptionHandler();
124 // Start update info fetching
125 final UpdateInfoRetriever updateInfo;
126 if ( Application.getPreferences().getCheckUpdates()) {
127 log.info("Starting update check");
128 updateInfo = new UpdateInfoRetriever();
131 log.info("Update check disabled");
135 // Set the best available look-and-feel
136 log.info("Setting best LAF");
137 GUIUtil.setBestLAF();
139 // Set tooltip delay time. Tooltips are used in MotorChooserDialog extensively.
140 ToolTipManager.sharedInstance().setDismissDelay(30000);
143 ((SwingPreferences) Application.getPreferences()).loadDefaultUnits();
146 log.info("Loading databases");
148 Databases.fakeMethod();
152 } catch ( InterruptedException iex) {
156 // Starting action (load files or open new document)
157 log.info("Opening main application window");
158 if (!handleCommandLine(args)) {
159 BasicFrame.newAction();
162 // Check whether update info has been fetched or whether it needs more time
163 log.info("Checking update status");
164 checkUpdateStatus(updateInfo);
166 // Block motor loading for 1.5 seconds to allow window painting to be faster
167 blockLoading.set(1500);
172 * Check that the JRE is not running headless.
174 private static void checkHead() {
176 if (GraphicsEnvironment.isHeadless()) {
177 log.error("Application is headless.");
178 System.err.println();
179 System.err.println("OpenRocket cannot currently be run without the graphical " +
181 System.err.println();
188 private static void loadMotor() {
190 log.info("Starting motor loading from " + THRUSTCURVE_DIRECTORY + " in background thread.");
191 ThrustCurveMotorSetDatabase db = new ThrustCurveMotorSetDatabase(true) {
194 protected void loadMotors() {
196 // Block loading until timeout occurs or database is taken into use
197 log.info("Blocking motor loading while starting up");
198 while (!inUse && blockLoading.addAndGet(-100) > 0) {
201 } catch (InterruptedException e) {
204 log.info("Blocking ended, inUse=" + inUse + " blockLoading=" + blockLoading.get());
207 log.info("Loading motors from " + THRUSTCURVE_DIRECTORY);
208 long t0 = System.currentTimeMillis();
210 int thrustCurveCount;
212 // Load the packaged thrust curves
214 FileIterator iterator = DirectoryIterator.findDirectory(THRUSTCURVE_DIRECTORY,
215 new SimpleFileFilter("", false, "eng", "rse"));
216 if (iterator == null) {
217 throw new IllegalStateException("Thrust curve directory " + THRUSTCURVE_DIRECTORY +
218 "not found, distribution built wrong");
220 list = MotorLoaderHelper.load(iterator);
221 for (Motor m : list) {
222 this.addMotor((ThrustCurveMotor) m);
224 fileCount = iterator.getFileCount();
226 thrustCurveCount = list.size();
228 // Load the user-defined thrust curves
229 for (File file : ((SwingPreferences) Application.getPreferences()).getUserThrustCurveFiles()) {
230 log.info("Loading motors from " + file);
231 list = MotorLoaderHelper.load(file);
232 for (Motor m : list) {
233 this.addMotor((ThrustCurveMotor) m);
236 thrustCurveCount += list.size();
239 long t1 = System.currentTimeMillis();
242 int distinctMotorCount = 0;
243 int distinctThrustCurveCount = 0;
244 distinctMotorCount = motorSets.size();
245 for (ThrustCurveMotorSet set : motorSets) {
246 distinctThrustCurveCount += set.getMotorCount();
248 log.info("Motor loading done, took " + (t1 - t0) + " ms to load "
249 + fileCount + " files/directories containing "
250 + thrustCurveCount + " thrust curves which contained "
251 + distinctMotorCount + " distinct motors with "
252 + distinctThrustCurveCount + " distinct thrust curves.");
257 Application.setMotorSetDatabase(db);
260 private static void checkUpdateStatus(final UpdateInfoRetriever updateInfo) {
261 if (updateInfo == null)
265 if (!updateInfo.isRunning())
268 final Timer timer = new Timer(delay, null);
270 ActionListener listener = new ActionListener() {
271 private int count = 5;
274 public void actionPerformed(ActionEvent e) {
275 if (!updateInfo.isRunning()) {
278 String current = BuildProperties.getVersion();
279 String last = Application.getPreferences().getString(Preferences.LAST_UPDATE, "");
281 UpdateInfo info = updateInfo.getUpdateInfo();
282 if (info != null && info.getLatestVersion() != null &&
283 !current.equals(info.getLatestVersion()) &&
284 !last.equals(info.getLatestVersion())) {
286 UpdateInfoDialog infoDialog = new UpdateInfoDialog(info);
287 infoDialog.setVisible(true);
288 if (infoDialog.isReminderSelected()) {
289 Application.getPreferences().putString(Preferences.LAST_UPDATE, "");
291 Application.getPreferences().putString(Preferences.LAST_UPDATE, info.getLatestVersion());
300 timer.addActionListener(listener);
304 private static class ComponentPresetLoader implements Callable {
306 CountDownLatch latch;
307 ComponentPresetDatabase componentPresetDao;
309 private ComponentPresetLoader( CountDownLatch latch, ComponentPresetDatabase componentPresetDao ) {
310 this.componentPresetDao = componentPresetDao;
315 public Object call() throws Exception {
316 componentPresetDao.load("datafiles/presets", "(?i).*orc");
324 * Handles arguments passed from the command line. This may be used either
325 * when starting the first instance of OpenRocket or later when OpenRocket is
326 * executed again while running.
328 * @param args the command-line arguments.
329 * @return whether a new frame was opened or similar user desired action was
330 * performed as a result.
332 private static boolean handleCommandLine(String[] args) {
334 // Check command-line for files
335 boolean opened = false;
336 for (String file : args) {
337 if (BasicFrame.open(new File(file), null)) {