Modified in the Past, Previously Known as the Future
Commons Configuration is a huge timesaver—flexible, simple configuration management with the highly useful feature of automatic file reloading. But, lately, that highly useful feature hadn’t been working for me. I’d make a change to the config file in a deployed application and see no change to the values in the application. WTF? What’s crazy is that it’d be inconsistent—some days it would work, others it wouldn’t. Yesterday the problem came to a head—I needed the reloading functionality to work, but it just wasn’t happening. So, I put together some simple test code and, viola, it worked! Huh? Back to the deployed application: no workie. Back to the test case: works like a charm. Egad!
Some JavaDoc digging led me to realize that the problem might be something screwy with the file last modification times. Sure enough, some of the deployed config file unix mtimes were in the future. Digging into the Commons code revealed that a file is only reloaded if the current mtime is ahead of the initial mtime of the file. So, Configuration wouldn’t reload the file until the present had caught up with the future. Ack! But, where were these future timestamps coming from? Ah, of course… (with gritted teeth) Maven. Turns out that the Maven packaging process was storing UTC timestamps in the war file. Yet, when we exploded the war file using unzip, those timestamps were not converted back to our local (EDT) time, so any file modified around the time of the packaging ended up 4 hours in the future.
The solution turned out to be simple: export TZ=”America/New_York”. This caused maven to store our local, rather than UTC times. Unzip’s behavior was unaffected by the setting of TZ, so the deployed files had the correct timestamps after all. A little test clarifies why Maven is doing this. Maven uses java code to perform the packaging and the java File.lastModified() method returns the UTC timestamp. What’s a bit appalling is that my local timezone is specified on my local system in the usual way: via /etc/localtime. Basic unix utilities like ls, stat and unzip don’t seem to have any problem doing the conversion. But, I don’t think Maven can be blamed since it’s probably doing the reasonable thing and using java.util.TimeZone.getDefault() to determine the time zone. A bit of searching reveals that the blame lays squarely on Sun’s head for using a poor hack to determine timezone on Linux. Amazingly, the bug describing this problem has been outstanding for over two years. Sun, is it really that hard to parse an /etc/localtime file?