diff --git a/android/src/net/sf/openrocket/android/Application.java b/android/src/net/sf/openrocket/android/Application.java index 624df23be..9661f400d 100644 --- a/android/src/net/sf/openrocket/android/Application.java +++ b/android/src/net/sf/openrocket/android/Application.java @@ -28,7 +28,12 @@ public class Application extends android.app.Application { net.sf.openrocket.startup.Application.setPreferences( new PreferencesAdapter() ); - net.sf.openrocket.startup.Application.setComponentPresetDao( new ComponentPresetDatabase() ); + net.sf.openrocket.startup.Application.setComponentPresetDao( new ComponentPresetDatabase(){ + @Override + protected void load() { + // We don't need components + } + } ); MotorDatabaseAdapter db = new MotorDatabaseAdapter(this); diff --git a/core/src/net/sf/openrocket/database/ComponentPresetDatabase.java b/core/src/net/sf/openrocket/database/ComponentPresetDatabase.java index 8acafcff5..91cfe609f 100644 --- a/core/src/net/sf/openrocket/database/ComponentPresetDatabase.java +++ b/core/src/net/sf/openrocket/database/ComponentPresetDatabase.java @@ -4,17 +4,34 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.startup.Application; -public class ComponentPresetDatabase extends Database implements ComponentPresetDao { +public abstract class ComponentPresetDatabase extends Database implements ComponentPresetDao { + + private static final LogHelper logger = Application.getLogger(); + + private volatile boolean startedLoading = false; + private volatile boolean endedLoading = false; + private final boolean asynchronous; + + /** Set to true the first time {@link #blockUntilLoaded()} is called. */ + protected volatile boolean inUse = false; public ComponentPresetDatabase() { super(); + this.asynchronous = false; + } + + public ComponentPresetDatabase(boolean asynchronous ) { + super(); + this.asynchronous = asynchronous; } @Override public List listAll() { + blockUntilLoaded(); return list; } @@ -25,6 +42,7 @@ public class ComponentPresetDatabase extends Database implement @Override public List listForType( ComponentPreset.Type type ) { + blockUntilLoaded(); if ( type == null ) { return Collections.emptyList(); } @@ -50,6 +68,7 @@ public class ComponentPresetDatabase extends Database implement */ @Override public List listForType( ComponentPreset.Type type, boolean favorite ) { + blockUntilLoaded(); if ( !favorite ) { return listForType(type); @@ -67,6 +86,7 @@ public class ComponentPresetDatabase extends Database implement @Override public List listForTypes( ComponentPreset.Type ... type ) { + blockUntilLoaded(); if( type == null || type.length == 0 ) { return Collections.emptyList(); @@ -93,11 +113,13 @@ public class ComponentPresetDatabase extends Database implement @Override public List listForTypes( List types ) { + blockUntilLoaded(); return listForTypes( (ComponentPreset.Type[]) types.toArray() ); } @Override public List find(String manufacturer, String partNo) { + blockUntilLoaded(); List presets = new ArrayList(); for( ComponentPreset preset : list ) { if ( preset.getManufacturer().getSimpleName().equals(manufacturer) && preset.getPartNo().equals(partNo) ) { @@ -109,9 +131,71 @@ public class ComponentPresetDatabase extends Database implement @Override public void setFavorite( ComponentPreset preset, boolean favorite ) { + blockUntilLoaded(); preset.setFavorite(favorite); Application.getPreferences().setComponentFavorite( preset, favorite ); this.fireAddEvent(preset); } + + /** + * Used for loading the component preset database. This method will be called in a background + * thread to load the presets asynchronously. + */ + protected abstract void load(); + + /** + * Start loading the presets. + * + * @throws IllegalStateException if this method has already been called. + */ + public void startLoading() { + if (startedLoading) { + throw new IllegalStateException("Already called startLoading"); + } + startedLoading = true; + if (asynchronous) { + new LoadingThread().start(); + } else { + load(); + } + synchronized (this) { + endedLoading = true; + this.notifyAll(); + } + } + + /** + * Background thread for loading the presets. + */ + private class LoadingThread extends Thread { + @Override + public void run() { + load(); + } + } + + /** + * Block the current thread until loading of the presets has been completed. + * + * @throws IllegalStateException if startLoading() has not been called. + */ + public void blockUntilLoaded() { + inUse = true; + if (!startedLoading) { + throw new IllegalStateException("startLoading() has not been called"); + } + if (!endedLoading) { + synchronized (this) { + while (!endedLoading) { + try { + this.wait(); + } catch (InterruptedException e) { + logger.warn("InterruptedException occurred, ignoring", e); + } + } + } + } + } + } diff --git a/core/src/net/sf/openrocket/gui/figure3d/Quick3dMain.java b/core/src/net/sf/openrocket/gui/figure3d/Quick3dMain.java index 49dbbc9fd..d7c2e842c 100644 --- a/core/src/net/sf/openrocket/gui/figure3d/Quick3dMain.java +++ b/core/src/net/sf/openrocket/gui/figure3d/Quick3dMain.java @@ -41,8 +41,14 @@ public class Quick3dMain { Application.setPreferences(new SwingPreferences()); // Must be done after localization is initialized - ComponentPresetDatabase componentPresetDao = new ComponentPresetDatabase(); - componentPresetDao.load("datafiles", ".*csv"); + ComponentPresetDatabase componentPresetDao = new ComponentPresetDatabase() { + + @Override + protected void load() { + // This test app doesn't need any presets loaded - just an empty database. + } + + }; Application.setComponentPresetDao( componentPresetDao ); OpenRocketDocument doc = new OpenRocketLoader().loadFromStream( diff --git a/core/src/net/sf/openrocket/startup/ConcurrentLoadingThrustCurveMotorSetDatabase.java b/core/src/net/sf/openrocket/startup/ConcurrentLoadingThrustCurveMotorSetDatabase.java index a546f739e..c52d72e19 100644 --- a/core/src/net/sf/openrocket/startup/ConcurrentLoadingThrustCurveMotorSetDatabase.java +++ b/core/src/net/sf/openrocket/startup/ConcurrentLoadingThrustCurveMotorSetDatabase.java @@ -9,6 +9,7 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import net.sf.openrocket.database.ThrustCurveMotorSet; import net.sf.openrocket.database.ThrustCurveMotorSetDatabase; @@ -47,6 +48,9 @@ public class ConcurrentLoadingThrustCurveMotorSetDatabase extends ThrustCurveMot private static final LogHelper log = Application.getLogger(); private final String thrustCurveDirectory; + /** Block motor loading for this many milliseconds */ + // Block motor loading for 1.5 seconds to allow window painting to be faster + private static AtomicInteger blockLoading = new AtomicInteger(1500); public ConcurrentLoadingThrustCurveMotorSetDatabase(String thrustCurveDirectory) { // configure ThrustCurveMotorSetDatabase as true so we get our own thread in @@ -58,6 +62,18 @@ public class ConcurrentLoadingThrustCurveMotorSetDatabase extends ThrustCurveMot @Override protected void loadMotors() { + // Block loading until timeout occurs or database is taken into use + log.info("Blocking motor loading while starting up"); + /* + while (!inUse && blockLoading.addAndGet(-100) > 0) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } + */ + log.info("Blocking ended, inUse=" + inUse + " blockLoading=" + blockLoading.get()); + BookKeeping keeper = new BookKeeping(); keeper.start(); @@ -162,9 +178,9 @@ public class ConcurrentLoadingThrustCurveMotorSetDatabase extends ThrustCurveMot private void waitForFinish() throws InterruptedException { try { loaderPool.shutdown(); - loaderPool.awaitTermination(10, TimeUnit.SECONDS); + loaderPool.awaitTermination(30, TimeUnit.SECONDS); writerThread.shutdown(); - writerThread.awaitTermination(10, TimeUnit.SECONDS); + writerThread.awaitTermination(30, TimeUnit.SECONDS); } finally { iterator.close(); diff --git a/core/src/net/sf/openrocket/startup/Startup2.java b/core/src/net/sf/openrocket/startup/Startup2.java index 85500a361..6b7b49cdf 100644 --- a/core/src/net/sf/openrocket/startup/Startup2.java +++ b/core/src/net/sf/openrocket/startup/Startup2.java @@ -84,11 +84,23 @@ public class Startup2 { Splash.init(); // Must be done after localization is initialized - ComponentPresetDatabase componentPresetDao = new ComponentPresetDatabase(); - ConcurrentComponentPresetDatabaseLoader presetLoader = new ConcurrentComponentPresetDatabaseLoader( componentPresetDao ); - presetLoader.load(); - + ComponentPresetDatabase componentPresetDao = new ComponentPresetDatabase(true) { + + @Override + protected void load() { + ConcurrentComponentPresetDatabaseLoader presetLoader = new ConcurrentComponentPresetDatabaseLoader( this ); + presetLoader.load(); + try { + presetLoader.await(); + } catch ( InterruptedException iex) { + + } + } + + }; Application.setComponentPresetDao( componentPresetDao ); + + componentPresetDao.startLoading(); // Setup the uncaught exception handler log.info("Registering exception handler"); @@ -124,11 +136,6 @@ public class Startup2 { Databases.fakeMethod(); - try { - presetLoader.await(); - } catch ( InterruptedException iex) { - - } // Starting action (load files or open new document) log.info("Opening main application window");