]> git.gag.com Git - debian/openrocket/blob - src/net/sf/openrocket/startup/Startup.java
971a5e94c6bc79cda85f4388fae4fb4c6e0dcea7
[debian/openrocket] / src / net / sf / openrocket / startup / Startup.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.io.IOException;
8 import java.io.InputStream;
9 import java.io.PrintStream;
10 import java.util.List;
11 import java.util.concurrent.atomic.AtomicInteger;
12
13 import javax.swing.JOptionPane;
14 import javax.swing.SwingUtilities;
15 import javax.swing.Timer;
16 import javax.swing.ToolTipManager;
17
18 import net.sf.openrocket.communication.UpdateInfo;
19 import net.sf.openrocket.communication.UpdateInfoRetriever;
20 import net.sf.openrocket.database.Databases;
21 import net.sf.openrocket.database.ThrustCurveMotorSet;
22 import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
23 import net.sf.openrocket.file.DirectoryIterator;
24 import net.sf.openrocket.file.GeneralMotorLoader;
25 import net.sf.openrocket.gui.dialogs.UpdateInfoDialog;
26 import net.sf.openrocket.gui.main.BasicFrame;
27 import net.sf.openrocket.gui.main.ExceptionHandler;
28 import net.sf.openrocket.gui.main.SimpleFileFilter;
29 import net.sf.openrocket.gui.main.Splash;
30 import net.sf.openrocket.logging.DelegatorLogger;
31 import net.sf.openrocket.logging.LogHelper;
32 import net.sf.openrocket.logging.LogLevel;
33 import net.sf.openrocket.logging.LogLevelBufferLogger;
34 import net.sf.openrocket.logging.PrintStreamLogger;
35 import net.sf.openrocket.motor.Motor;
36 import net.sf.openrocket.motor.ThrustCurveMotor;
37 import net.sf.openrocket.util.GUIUtil;
38 import net.sf.openrocket.util.Pair;
39 import net.sf.openrocket.util.Prefs;
40
41
42 /**
43  * A startup class that checks that a suitable JRE environment is being run.
44  * If the environment is too old the execution is canceled, and if OpenJDK is being
45  * used warns the user of problems and confirms whether to continue.
46  * 
47  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
48  */
49 public class Startup {
50         
51         private static LogHelper log;
52         
53         private static final String LOG_STDERR_PROPERTY = "openrocket.log.stderr";
54         private static final String LOG_STDOUT_PROPERTY = "openrocket.log.stdout";
55         
56         private static final int LOG_BUFFER_LENGTH = 50;
57         
58         private static final String THRUSTCURVE_DIRECTORY = "datafiles/thrustcurves/";
59         
60
61         /** Block motor loading for this many milliseconds */
62         private static AtomicInteger blockLoading = new AtomicInteger(Integer.MAX_VALUE);
63         
64         
65         public static void main(final String[] args) throws Exception {
66                 
67                 // Initialize logging first so we can use it
68                 initializeLogging();
69                 
70                 // Check that we have a head
71                 checkHead();
72                 
73                 // Check that we're running a good version of a JRE
74                 log.info("Checking JRE compatibility");
75                 VersionHelper.checkVersion();
76                 VersionHelper.checkOpenJDK();
77                 
78                 // Run the actual startup method in the EDT since it can use progress dialogs etc.
79                 log.info("Running main");
80                 SwingUtilities.invokeAndWait(new Runnable() {
81                         @Override
82                         public void run() {
83                                 runMain(args);
84                         }
85                 });
86                 
87                 log.info("Startup complete");
88                 
89                 // Block motor loading for 2 seconds to allow window painting
90                 blockLoading.set(2000);
91         }
92         
93         
94
95
96         private static void runMain(String[] args) {
97                 
98                 // Initialize the splash screen with version info
99                 log.info("Initializing the splash screen");
100                 Splash.init();
101                 
102                 // Setup the uncaught exception handler
103                 log.info("Registering exception handler");
104                 ExceptionHandler.registerExceptionHandler();
105                 
106                 // Start update info fetching
107                 final UpdateInfoRetriever updateInfo;
108                 if (Prefs.getCheckUpdates()) {
109                         log.info("Starting update check");
110                         updateInfo = new UpdateInfoRetriever();
111                         updateInfo.start();
112                 } else {
113                         log.info("Update check disabled");
114                         updateInfo = null;
115                 }
116                 
117                 // Set the best available look-and-feel
118                 log.info("Setting best LAF");
119                 GUIUtil.setBestLAF();
120                 
121                 // Set tooltip delay time.  Tooltips are used in MotorChooserDialog extensively.
122                 ToolTipManager.sharedInstance().setDismissDelay(30000);
123                 
124                 // Load defaults
125                 Prefs.loadDefaultUnits();
126                 
127                 // Load motors etc.
128                 // TODO: HIGH: Use new motor loading
129                 log.info("Loading databases");
130                 loadMotor();
131                 Databases.fakeMethod();
132                 
133                 // Starting action (load files or open new document)
134                 log.info("Opening main application window");
135                 if (!handleCommandLine(args)) {
136                         BasicFrame.newAction();
137                 }
138                 
139                 // Check whether update info has been fetched or whether it needs more time
140                 log.info("Checking update status");
141                 checkUpdateStatus(updateInfo);
142         }
143         
144         
145
146         private static void loadMotor() {
147                 
148                 log.info("Starting motor loading from " + THRUSTCURVE_DIRECTORY +
149                                 " in background thread.");
150                 ThrustCurveMotorSetDatabase db = new ThrustCurveMotorSetDatabase(true) {
151                         
152                         @Override
153                         protected void loadMotors() {
154                                 
155                                 log.info("Blocking motor loading while starting up");
156                                 
157                                 // Block for 100ms a time until timeout or database in use
158                                 while (!inUse && blockLoading.addAndGet(-100) > 0) {
159                                         try {
160                                                 Thread.sleep(100);
161                                         } catch (InterruptedException e) {
162                                         }
163                                 }
164                                 
165                                 log.info("Blocking ended, inUse=" + inUse + " slowLoadingCount=" + blockLoading.get());
166                                 
167                                 log.info("Started to load motors from " + THRUSTCURVE_DIRECTORY);
168                                 long t0 = System.currentTimeMillis();
169                                 
170                                 int fileCount = 0;
171                                 int thrustCurveCount = 0;
172                                 int distinctMotorCount = 0;
173                                 int distinctThrustCurveCount = 0;
174                                 
175                                 GeneralMotorLoader loader = new GeneralMotorLoader();
176                                 DirectoryIterator iterator = DirectoryIterator.findDirectory(THRUSTCURVE_DIRECTORY,
177                                                                 new SimpleFileFilter("", false, "eng", "rse"));
178                                 if (iterator == null) {
179                                         throw new IllegalStateException("No thrust curves found, distribution built wrong");
180                                 }
181                                 while (iterator.hasNext()) {
182                                         final Pair<String, InputStream> input = iterator.next();
183                                         log.debug("Loading motors from file " + input.getU());
184                                         fileCount++;
185                                         try {
186                                                 List<Motor> motors = loader.load(input.getV(), input.getU());
187                                                 if (motors.size() == 0) {
188                                                         log.warn("No motors found in file " + input.getU());
189                                                 }
190                                                 for (Motor m : motors) {
191                                                         thrustCurveCount++;
192                                                         this.addMotor((ThrustCurveMotor) m);
193                                                 }
194                                         } catch (IOException e) {
195                                                 log.warn("IOException when loading motor file " + input.getU(), e);
196                                         } finally {
197                                                 try {
198                                                         input.getV().close();
199                                                 } catch (IOException e) {
200                                                         log.error("IOException when closing InputStream", e);
201                                                 }
202                                         }
203                                         
204                                 }
205                                 
206                                 long t1 = System.currentTimeMillis();
207                                 
208                                 // Count statistics
209                                 distinctMotorCount = motorSets.size();
210                                 for (ThrustCurveMotorSet set : motorSets) {
211                                         distinctThrustCurveCount += set.getMotorCount();
212                                 }
213                                 log.info("Motor loading done, took " + (t1 - t0) + " ms to load "
214                                                 + fileCount + " files containing " + thrustCurveCount + " thrust curves which contained "
215                                                 + distinctMotorCount + " distinct motors with " + distinctThrustCurveCount + " thrust curves.");
216                         }
217                         
218                 };
219                 db.startLoading();
220                 Application.setMotorSetDatabase(db);
221         }
222         
223         
224
225         private static void checkUpdateStatus(final UpdateInfoRetriever updateInfo) {
226                 if (updateInfo == null)
227                         return;
228                 
229                 int delay = 1000;
230                 if (!updateInfo.isRunning())
231                         delay = 100;
232                 
233                 final Timer timer = new Timer(delay, null);
234                 
235                 ActionListener listener = new ActionListener() {
236                         private int count = 5;
237                         
238                         @Override
239                         public void actionPerformed(ActionEvent e) {
240                                 if (!updateInfo.isRunning()) {
241                                         timer.stop();
242                                         
243                                         String current = Prefs.getVersion();
244                                         String last = Prefs.getString(Prefs.LAST_UPDATE, "");
245                                         
246                                         UpdateInfo info = updateInfo.getUpdateInfo();
247                                         if (info != null && info.getLatestVersion() != null &&
248                                                         !current.equals(info.getLatestVersion()) &&
249                                                         !last.equals(info.getLatestVersion())) {
250                                                 
251                                                 UpdateInfoDialog infoDialog = new UpdateInfoDialog(info);
252                                                 infoDialog.setVisible(true);
253                                                 if (infoDialog.isReminderSelected()) {
254                                                         Prefs.putString(Prefs.LAST_UPDATE, "");
255                                                 } else {
256                                                         Prefs.putString(Prefs.LAST_UPDATE, info.getLatestVersion());
257                                                 }
258                                         }
259                                 }
260                                 count--;
261                                 if (count <= 0)
262                                         timer.stop();
263                         }
264                 };
265                 timer.addActionListener(listener);
266                 timer.start();
267         }
268         
269         
270         /**
271          * Handles arguments passed from the command line.  This may be used either
272          * when starting the first instance of OpenRocket or later when OpenRocket is
273          * executed again while running.
274          * 
275          * @param args  the command-line arguments.
276          * @return              whether a new frame was opened or similar user desired action was
277          *                              performed as a result.
278          */
279         public static boolean handleCommandLine(String[] args) {
280                 
281                 // Check command-line for files
282                 boolean opened = false;
283                 for (String file : args) {
284                         if (BasicFrame.open(new File(file), null)) {
285                                 opened = true;
286                         }
287                 }
288                 return opened;
289         }
290         
291         
292
293         /**
294          * Check that the JRE is not running headless.
295          */
296         private static void checkHead() {
297                 
298                 log.info("Checking for graphics head");
299                 
300                 if (GraphicsEnvironment.isHeadless()) {
301                         log.error("Application is headless.");
302                         System.err.println();
303                         System.err.println("OpenRocket cannot currently be run without the graphical " +
304                                         "user interface.");
305                         System.err.println();
306                         System.exit(1);
307                 }
308                 
309         }
310         
311         
312         ///////////  Logging  ///////////
313         
314         private static void initializeLogging() {
315                 DelegatorLogger delegator = new DelegatorLogger();
316                 
317                 // Log buffer
318                 LogLevelBufferLogger buffer = new LogLevelBufferLogger(LOG_BUFFER_LENGTH);
319                 delegator.addLogger(buffer);
320                 
321                 // Check whether to log to stdout/stderr
322                 PrintStreamLogger printer = new PrintStreamLogger();
323                 boolean logout = setLogOutput(printer, System.out, System.getProperty(LOG_STDOUT_PROPERTY), null);
324                 boolean logerr = setLogOutput(printer, System.err, System.getProperty(LOG_STDERR_PROPERTY), LogLevel.WARN);
325                 if (logout || logerr) {
326                         delegator.addLogger(printer);
327                 }
328                 
329                 // Set the loggers
330                 Application.setLogger(delegator);
331                 Application.setLogBuffer(buffer);
332                 
333                 // Initialize the log for this class
334                 log = Application.getLogger();
335                 log.info("Logging subsystem initialized for OpenRocket " + Prefs.getVersion());
336                 String str = "Console logging output:";
337                 for (LogLevel l : LogLevel.values()) {
338                         PrintStream ps = printer.getOutput(l);
339                         str += " " + l.name() + ":";
340                         if (ps == System.err) {
341                                 str += "stderr";
342                         } else if (ps == System.out) {
343                                 str += "stdout";
344                         } else {
345                                 str += "none";
346                         }
347                 }
348                 str += " (" + LOG_STDOUT_PROPERTY + "=" + System.getProperty(LOG_STDOUT_PROPERTY) +
349                                 " " + LOG_STDERR_PROPERTY + "=" + System.getProperty(LOG_STDERR_PROPERTY) + ")";
350                 log.info(str);
351         }
352         
353         private static boolean setLogOutput(PrintStreamLogger logger, PrintStream stream, String level, LogLevel defaultLevel) {
354                 LogLevel minLevel = LogLevel.fromString(level, defaultLevel);
355                 if (minLevel == null) {
356                         return false;
357                 }
358                 
359                 for (LogLevel l : LogLevel.values()) {
360                         if (l.atLeast(minLevel)) {
361                                 logger.setOutput(l, stream);
362                         }
363                 }
364                 return true;
365         }
366         
367         
368         ///////////  Helper methods  //////////
369         
370         /**
371          * Presents an error message to the user and exits the application.
372          * 
373          * @param message       an array of messages to present.
374          */
375         static void error(String[] message) {
376                 
377                 System.err.println();
378                 System.err.println("Error starting OpenRocket:");
379                 System.err.println();
380                 for (int i = 0; i < message.length; i++) {
381                         System.err.println(message[i]);
382                 }
383                 System.err.println();
384                 
385
386                 if (!GraphicsEnvironment.isHeadless()) {
387                         
388                         JOptionPane.showMessageDialog(null, message, "Error starting OpenRocket",
389                                         JOptionPane.ERROR_MESSAGE);
390                         
391                 }
392                 
393                 System.exit(1);
394         }
395         
396         
397         /**
398          * Presents the user with a message dialog and asks whether to continue.
399          * If the user does not select "Yes" the the application exits.
400          * 
401          * @param message       the message Strings to show.
402          */
403         static void confirm(String[] message) {
404                 
405                 if (!GraphicsEnvironment.isHeadless()) {
406                         
407                         if (JOptionPane.showConfirmDialog(null, message, "Error starting OpenRocket",
408                                         JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
409                                 System.exit(1);
410                         }
411                 }
412         }
413         
414 }