Friday, February 29, 2008

Generic On-The-Fly Deployment

Once spoiled by dynamic-languages web frameworks, it is hard to go back to one where a manual build cycle is needed before seeing the changes made to the source code on the running application. It must be instantaneous.

As you can infer from my latest posts, I am now working with Flex. And Java Servlets, using Resin as the server. That environment is not even close to be as developer-friendly as Django, or Rails or many others modern web frameworks. Perhaps I am being a bit unfair, because Resin tries to be developer-friendly, not only reloading the context when something changes, but also providing a special directory where you can put your java sources and it even compiles them. But the project layout is not configured the way Resin likes it, so it did not work.

Other developers use a IDE plugin to deploy the compiled classes automatically. But, is really an IDE needed to simply (re)build a project after some file(s) changes? I do not think so. After all, it is just matter of monitoring the file system and firing the build process if a change is detected. So I came with a simple, handy utility, which monitor a specified directory and run an arbitrary command when a change is detected.

Integrating it with our build script is trivial:

> cd \eclipse3.3\eclipse\workspace\MyProject
> python dirwatch.py -c "ant deploy-development"

Moreover, Eclipse integration is easy too: Run-> External Tools -> Open External Tools Dialog -> Program -> New:

Location: c:\python25\python.exe
Working Directory: ${workspace_loc:/MyProject}
Arguments: -u "c:\path\to\dirwatch.py" -c "ant deploy-development"

The whole point of the exercise? Decouple the convenience of automatic deploymeny from a particular IDE, where it does not belong. Just like building.

So here is the program (currently only works on Windows, but should be easy to port to Unix-like systems, using inotify or FAM):
#!/usr/bin/env python
"""
dirwatch:

Monitor a directory for changes. Executes a command after a change is detected.
"""

import os
import time
import win32file
import win32event
import win32con
from optparse import OptionParser

VERSION = "1.0"

def dirwatch(path_to_watch, seconds_to_wait, commands):
print time.asctime(), "Watching %s" % path_to_watch
import sys
change_handle = win32file.FindFirstChangeNotification (
path_to_watch,
1, # => recursive
win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
win32con.FILE_NOTIFY_CHANGE_SIZE |
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE
)
# Loop forever, listing any file changes.
try:
last_change_time = None
while True:
result = win32event.WaitForSingleObject(change_handle, 1000)
if result == win32con.WAIT_OBJECT_0:
if last_change_time:
if time.time() - last_change_time > seconds_to_wait:
msg = "More change(s) detected, " \
"waiting %d more second(s)"
else:
msg = None
else:
msg = "Change detected, waiting %d second(s)"
if msg:
print time.asctime(), msg % seconds_to_wait
last_change_time = time.time()
win32file.FindNextChangeNotification(change_handle)
if last_change_time:
if time.time() - last_change_time > seconds_to_wait:
for command in commands:
print time.asctime(), "Executing '%s'" % command
os.system(command)
last_change_time = None
finally:
win32file.FindCloseChangeNotification(change_handle)


def parse_options(args):
parser = OptionParser(usage="%prog [-c command[;command...]] [directory]",
version="%prog " + VERSION)

parser.add_option("-w", "--wait", dest="seconds_to_wait", metavar="SECONDS",
default="1",
help="seconds to wait after last change before the "
"of the command(s).")
parser.add_option("-c", "--command", dest="command",
default="echo change detected",
help="command to execute after a change is detected")
return parser.parse_args(args)
def main(args):
options, args = parse_options(args)
try: path_to_watch = args[1] or "."
except: path_to_watch = "."
dirwatch(path_to_watch, int(options.seconds_to_wait),
options.command.split(';'))

if __name__ == "__main__":
import sys
main(sys.argv)

Tuesday, February 26, 2008

Flex Builder 2 on top of Eclipse 3.3

The Flex Builder installer does not like Eclipse 3.3. When you try to install it over eclipse, it says the destination folder is not valid.

I found some instructions on how to manually plug the FlexBuilder components in Eclipse 3.3, provided that you installed it as a stand alone application. But it contains a small, but important, error on the directory layout, because the "plugins" and "features" directories must be inside "your-new-extension-point/eclipse". That is, the directory structure inside the extension point should be:

./
/eclipse
/eclipse/.eclipseextension
/eclipse/plugins
/eclipse/features

Other than that, everything went OK following the instruction of the article. The only other different thing I did was to only copy the com.adobe.* plugins and features. But it worked either way.

Monday, February 25, 2008

A Closure-Related Gotcha on Flex 2 (AS3)

This is one of these write-it-so-you-will-never-forget blog posts. Because I spent much time spotting the bug on code that used closures to simplify the code. It is not practical to show the actual code here, so I will make a dumb "clone" of the relevant section:

function doSomething() {
for (var i = 0; i < 10; i++) {
var foo = makeFoo();
foo.addEventLister(ResultEvent.RESULT, timeLogger(i));
}
}

function timeLogger(i) {
return function(event) {
Logger.debug("Received request number ", i);
for(var i = 0; i < 100; i++) {
// For some reason this loop had to exist.
}
}
}

[If you are curious, I did not included the inner anonymous function inside doSomething, because all instances of the function would "close over" the same reference to i]

As you can see, the idea was to add some timers for some asynchronous events. See the bug? Yes? I envy you! No? Well, unless you have psychic debugging abilities, it is hard to say without knowing the observed, wrong behaviour: Every logged request had an "undefined" number. That i was always undefined. WTF!?

The bug is the formal parameter i of timeLogger conflicting with the loop variable i on the anonymous inner function. You change either variable name, and the problem is solved. Ta-da!

I suppose this is a side effect of the static analysis and typing made by the ActionScript compiler. This gotcha does not currently apply to JavaScript, because of good old interpreters "dumbness" prevent them to bind variables to definitions that are ahead on the code. But I question the fact that such "clever" idea is a good one. In fact, I do not see a single use case for it.