Is there a way to terminate a java application that uses java3d, without calling System.exit()? -
java3d starts several system threads , doesn't set isdaemon flag on them. when dispose (only) jframe of application won't terminate because these threads still running.
calling system.exit() seems way terminate application. (or killing outside, of course).
as don't call system.exit() have tried following (but without success):
- calling removealllocales() on virtualuniverse: terminates of threads, still there 1 (named j3d-renderer-1) remaining.
- using reflection obtain reference the field threadgroup rootthreadgroupp in javax.media.j3d.mastercontrol , settting isdeamon true on threadgroup. didn't seem have effect.
- geting reference threadgroup named "java3d" , calling interrupt() on it: caused java3d threads write interruptedexception stderr, nothing else.
- locate sources of java3d-core library , propose patch: found repository here: https://github.com/hharrison/java3d-core , here: https://java.net/projects/j3d-core/sources . later 1 looks "official" shows last change in happened 5 years ago , former 1 looks private fork me.
i close giving , make call system.exit(), still don't it. know better way?
one possible solution call java3dthread.finish()
method on java3d threads. drawback 1 has bypass java access rules call method, package-private. code did trick me:
public void dispose() { virtualuniverse.removealllocales(); try { // give java3d threads chance terminate peacefully. thread.sleep(250); } catch (final interruptedexception e) { thread.currentthread().interrupt(); } // , handle threads didn't take hint finishjava3dthreads(); } private static void finishjava3dthreads() { final class<?> rendererclass; final method finishmethod; try { rendererclass = class.forname("javax.media.j3d.j3dthread"); finishmethod = rendererclass.getdeclaredmethod("finish"); finishmethod.setaccessible(true); } catch (classnotfoundexception | nosuchmethodexception | securityexception e) { throw new runtimeexception(e); } final threadgroup[] groups = new threadgroup[10]; final int count = thread.currentthread().getthreadgroup().getparent().enumerate(groups); (int = 0; < count; i++) { final threadgroup threadgroup = groups[i]; if ("java3d".equals(threadgroup.getname())) { threadgroup.setdaemon(true); final thread[] threads = new thread[threadgroup.activecount()]; final int threadcount = threadgroup.enumerate(threads); (int j = 0; j < threadcount; j++) { final thread thread = threads[j]; if (rendererclass.isinstance(thread)) { try { finishmethod.invoke(thread); } catch (illegalaccessexception | invocationtargetexception e) { throw new runtimeexception(e); } } } thread.yield(); threadgroup.interrupt(); } } }
where virtualuniverse instance of virtualuniverse created earlier in application.
now call dispose()
method terminate java3d when terminating application:
addwindowlistener(new windowadapter() { @override public void windowclosed(final windowevent e) { plater.dispose(); } });
where plater
instance containing dispose()
method above.
everything else disposes main jframe:
actions.put(exit_action, new abstractaction(exit_action) { @override public void actionperformed(final actionevent e) { dispose(); } });
and default close operation set dispose_on_close
:
setdefaultcloseoperation(windowconstants.dispose_on_close);
not sure if best option, though. (i still prefer on system.exit()
call, using reflection in way fragile.)
Comments
Post a Comment