There’s a small error in TestChooser.java that I need help with.
When the TestChooser is ran with setShowSettings, which starts the selected TestChooser list object with the display settings dialog of jme, and the user clicks Cancel rather than Continue, it leads to a infinite loop because context is never set.
//User has selected setShowSettings
if (app instanceof SimpleApplication) {
final Method settingMethod = clazz.getMethod("setShowSettings", boolean.class);
settingMethod.invoke(app, showSetting);
}
final Method mainMethod = clazz.getMethod("start");
//Jme settings dialog starts
mainMethod.invoke(app);
Field contextField = LegacyApplication.class.getDeclaredField("context");
contextField.setAccessible(true);
JmeContext context = null;
//User cancels rather than continues so context never gets set and we enter loop
//with null context and contextField.get() always returns null.
while (context == null) {
context = (JmeContext) contextField.get(app);
Thread.sleep(100);
}
What’s the best approach to determine that the user has canceled from the TestChooser thread?
I came up with a hack that doesn’t completely solve the problem but allows the TestChooser to continue working instead of freezing and leaving hanging threads, forcing the user to kill the app.
The TestChosser would just crash otherwise and not exit gracefully.
//Using CachedThreadPool and Futures allows the JDialog to continue if the
//user cancels SettingsDialog. Should be sufficient enough for the scope of
//this program.
private final ExecutorService executorService = Executors.newCachedThreadPool();
//Hack fix to prevent infinite loop caused when user
//cancels SettingsDialog rather than continuing, which
//prevents context from being set for selected app.
//Allows this JDialog to continue and shutdown cleaner.
Future<JmeContext> future = executorService.submit(() -> {
JmeContext context = null;
while (context == null) {
context = (JmeContext) contextField.get(app);
TimeUnit.MILLISECONDS.sleep(100);
}
return context;
});
JmeContext context = future.get();
Future<Boolean> future2 = executorService.submit(() -> {
while (!context.isCreated()) {
TimeUnit.MILLISECONDS.sleep(100);
}
return true;
});
future2.get();
Future<Boolean> future3 = executorService.submit(() -> {
while (context.isCreated()) {
TimeUnit.MILLISECONDS.sleep(100);
}
return true;
});
future3.get();
What happens with this is when the user cancels the SettingsDialog, the app will drop out of the loop its in like before but rather than crashing and leaving the app unresponsive, now the app is still alive, no exception thrown. User can carry on.
Once the app is closed, canceled or escaped it will throw an exception, close the TestChooser gracefully and restart any SettingsDialog that was canceled.
Then the user can do what the want with no side effects.
Its a hack. If this is just shit and should be done different please let me know how to improve it.
If not, I can post the entire file, which is updated to 1.8 standard, for more critique and to let you see how it works.
int count = 0;
while (context == null) {
context = (JmeContext) contextField.get(app);
Thread.sleep(100);
count++;
if (count >= 20) {
break;
}
}
if (context != null) {
while (!context.isCreated()) {
Thread.sleep(100);
}
while (context.isCreated()) {
Thread.sleep(100);
}
}
That’s to easy .
However, ill keep mine the way I have it, I don’t like seeing 100s of warnings when I open a file. Not to mention the original uses deprecated methods and obsolete list.