Saturday, August 10, 2013

Open Visual Trace Route 1.3

Open Visual Trace Route version 1.3 released.

* Upgrade to Worldwind 1.5.0, change layer panel, add graticule 
* Add Gantt view of the route 
* Add Replay function of the traceroute 
* Implement bi-directional selection synchronization map<->table 
* Focus on the last point of the route during tracing 
* Update visual of the Route (3d shape) 
* Update labels of the points (cities) of the route 
* Save application window size and split location when exiting the application 
* Highlight current route point during tracing (both map and table) 
* Fix an error when clearing selection the table 
* Fix an error that crashed the application when starting from a directory that has space characters inside its path 
* Fix a memory leak when tracing the route



Order of Shutdown Hook executions

In a previous post I talked about restarting a Java application programmatically.

Since that, I used that code on a couple of Java applications, and found out that the code didn't work well when using some libraries, especially if these libraries register a shutdown hook.

Why is that so ? One of the trick used to restart was to execute the start command in a shutdown hook, to be sure that everything is cleaned up (especially socket servers or objects that are a little sensitive regarding the allocation of a port or a given exclusive resource) before restarting.

The issue is that this trick only works if these objects clean up themselves before the restart command is executed, which is not necessarily the case if this clean up is done in another shutdown hook (which is the case for Jetty web server for example).
The reason for that is that Shutdown hooks executions are not ordered.

To convince yourself, you can take a look at the java.lang.ApplicationShutdownHooks source and see that the collection of shutdown hook threads is stored in an IdentityMap, which is unfortunately not ordered.

I make a parenthesis here, because seeing that code made me wonder how was then managed the File.deleteOnExit(); case that, I believed naively being just another shutdown hook.
Actually, their are different types of shutdown hooks, with different priorities, and these priorities are managed by the java.lang.Shutdown class. This class is pretty peculiar one, and by looking at its code, we learn that their are actually 3 types of shutdown hooks :
  • One fo the Console, to restore the echo.
  • One for hooks registered by the application through the Runtime.getRuntime().addShutdownHook(Thread)
  • And one for the File.deleteOnExit()
And we also learn that the number of types of hook shall not exceed 10 (why not just 3 ?!?, we don't know)
private static final int MAX_SYSTEM_HOOKS = 10;
private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS];
End of the parenthesis. We now understand a little more how all these things connect together, and can think of a solution for my Shutdown hook, that I want to be sure that it will be executed after the other ones, without knowing who (what other 3rd part library) register shutdown hooks.
The solution that I came up with, was to execute all the hooks manually, clear the IdentityMap, add my hook and finally System.exit(). This will give something like that :
runShutdownHooksNow();
Runtime.getRuntime().addShutdownHook(myHook);
System.exit(0);
Where the executeShutdownHooksNow() method will use reflection to run the hooks manually :
private void runShutdownHooksNow() {
 try {
  Field hooksField = Class.forName(
    "java.lang.ApplicationShutdownHooks").getDeclaredField(
    "hooks");
  hooksField.setAccessible(true);
  
  IdentityHashMap<Thread, Thread> currentHooks = (IdentityHashMap<Thread, Thread>) hooksField
    .get(null);
  for (Thread thread : currentHooks.keySet()) {
   try {
    thread.start();
    thread.join();
   } catch (InterruptedException e) {
   } catch (Throwable e) {
    // just print, if executed within a strandart hook it would be ignored anyway
    e.printStackTrace();
   }
  }
  currentHooks.clear();
 } catch (Exception e) {
  e.printStackTrace();
 }
}
With this solution, we ensure that our hook will be run last.