So I had an idea yesterday, and while drinking my coffee this morning decided to implement it.
I have been fighting trying to trace exceptions from enqueued tasks due to the heavy use of multithreading in Outside. So I had an idea of adding a wrapper around the enqueue task for tracing back the exception to the actual source of what added the task.
I figured I would share it so that it may be of use to others as well, perhaps it can be improved upon.
This is my standard jme wrapper for Outside with the implemented changes to enqueue:
public class JmeApplication extends SimpleApplication {
private final static Logger LOGGER = Logger.getLogger(Outside.class.getName());
private File appSettings = new File("graphics.properties");
@Override
public void simpleInitApp() {
this.getFlyByCamera().setEnabled(false);
this.setDisplayFps(false);
this.setDisplayStatView(false);
}
public void setPaused(boolean pause) {
this.paused = pause;
}
public boolean getPaused() {
return paused;
}
public void saveSettings() {
try {
settings.save(new FileOutputStream(appSettings));
} catch (Exception ex) {
LOGGER.log(Level.WARNING, "Failed to save settings", ex);
}
}
public void loadSettings() {
try {
if (appSettings.exists()) {
settings.load(new FileInputStream(appSettings));
} else {
settings.save(new FileOutputStream(appSettings));
}
} catch (Exception ex) {
LOGGER.log(Level.WARNING, "Failed to save settings", ex);
}
}
@Override
public <V> Future<V> enqueue(Callable<V> callable) {
final Throwable tracer = new Throwable("Enqueue Tracer");
Callable<V> wrapper = () -> {
try {
return callable.call();
} catch (Exception ex) {
ex.addSuppressed(tracer);
throw new RuntimeException("Jme Enqueued Task Exception", ex);
}
};
return super.enqueue(wrapper);
}
@Override
public void enqueue(Runnable runnable) {
final Throwable tracer = new Throwable("Enqueue Tracer");
Runnable wrapper = () -> {
try {
runnable.run();
} catch (Exception ex) {
ex.addSuppressed(tracer);
throw new RuntimeException("Jme Enqueued Task Exception", ex);
}
};
super.enqueue(wrapper);
}
}
A sample stack trace:
[2021-02-16 11:11:27] [SEVERE ] Exception
java.lang.RuntimeException: Jme Enqueued Task Exception
at io.tlf.outside.jme.JmeApplication.lambda$enqueue$0(JmeApplication.java:63)
at com.jme3.app.AppTask.invoke(AppTask.java:147)
at com.jme3.app.LegacyApplication.runQueuedTasks(LegacyApplication.java:733)
at com.jme3.app.LegacyApplication.update(LegacyApplication.java:748)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:247)
at com.jme3.system.lwjgl.LwjglWindow.runLoop(LwjglWindow.java:537)
at com.jme3.system.lwjgl.LwjglWindow.run(LwjglWindow.java:639)
at com.jme3.system.lwjgl.LwjglWindow.create(LwjglWindow.java:473)
at com.jme3.app.LegacyApplication.start(LegacyApplication.java:481)
at com.jme3.app.LegacyApplication.start(LegacyApplication.java:441)
at com.jme3.app.SimpleApplication.start(SimpleApplication.java:128)
at io.tlf.outside.client.Client.load(Client.java:87)
at io.tlf.outside.client.Main.launch(Main.java:133)
at io.tlf.outside.client.Main.main(Main.java:90)
Caused by: java.lang.RuntimeException: Test
at io.tlf.outside.client.Client.lambda$buildClient$0(Client.java:242)
at io.tlf.outside.jme.JmeApplication.lambda$enqueue$0(JmeApplication.java:60)
... 13 more
Suppressed: java.lang.Throwable: Enqueue Tracer
at io.tlf.outside.jme.JmeApplication.enqueue(JmeApplication.java:57)
at io.tlf.outside.client.Client.buildClient(Client.java:240)
at io.tlf.outside.client.Client$1.run(Client.java:78)
It gives me both the actual exception, and the Enqueue tracer to the code that added the task.
Just thought I would share, I hope this is useful to someone.