Saturday, June 12, 2010

Initial Paper Outline

The latest version will always be at this public link in Dropbox.

Tuesday, April 27, 2010

Bindings and Closures for script engine

So I've been working for a while to get the scripts that are run by the GroovyScriptEngine to have some more useful top level elements. In other words, I can write a script with the contents

and have something called "command" get run by the script. The very basic way to put things into the script's namespace is to call the GroovyScriptEngine.execute() command and pass it both the script you want to run and the Binding object. The binding object maps string names of things to values or objects which are available to the script. Setting variables in the binding to primitive values is easy but not hugely useful. Setting "thing" to "1" in the binding allows me to do things like

println thing

Not the most exciting. However, I've found that you can also set values in the binding to be more interesting things like a groovy Closure object. Since the code that is running the script and has access to the script engine itself is Java code I had to create an implementation of a Groovy Closure object, but once I did that I could set the closure to a variable in the binding and do things like


where closure is the name of the binding variable that I set for the closure object. It actually passes the argument to the closure itself so you can call almost anything that way.

Right now I have the closure looking up existing JMXTerm commands and passing the remaining variables to them to run them. This is pretty cool. I can run all of the existing Java implemented commands as well as calling any of the scripts that I wrote that can be found, with all of the infinite loops that that might imply by calling a script from itself.

So this is useful but still kind of ugly.

I tried another tack to remove the necessity of the top level "closure" in order to call a command. I want the command to be at the top level in the script. To do this I tried to override the Binding object itself.

public Object getVariable(String name) {
return super.getVariable(name);
} catch (MissingPropertyException e)
Boolean callReturn = (Boolean) Object[] { name });
if (!callReturn)
throw e;

return null;

With this overridden method from Binding, when the script is called and it goes to its binding for a variable, if it can't find it I can have it call the closure I made before which already calls the built in commands. So I can do things like


and have "command" be run. It goes to the Binding.getVariable() method, catches the resulting MissingMethod exception and then passes it to the closure. The real downside here is that I can't seem to pass any variables to the command. If I do that I get a completely different exception from the bowels if Groovy's abstractcallSite object which I don't appear to have the opportunity to catch. Kinda lame. If the line has an argument it doesn't look it up in the Binding but tries to call it like a method on the script itself, which, of course, doesn't exist.

So I can either have a top level command with no arguments, or a wrapper method like "closure" or maybe a more friendly "run" that can take arguments but is ugly.

I think I'm going to leave this like this as I've spent too much time mucking around with this already. I'm going to start writing the scripts now and see what I need. I'll be my own customer from now on.

Thursday, January 14, 2010

Script Engine operational

I think I'm all done with the embedding of the script engine itself. I have tests that are able to execute scripts by calling them by name at the JMXTerm command line. I decided to allow both no file extension and .groovy file extension, both callable by just the script name with no extension. Some editors, such as eclipse, fare much better when dealing with files with extensions as opposed to no extensions. Its a small thing might be nice for people writing scripts.

Next up is to decide what actually goes into the script binding to be made available to the scripts for use. Each command has access to a Session object which keeps track of all sorts of things such as open JMX connections, output and input streams for the command. Some of this would be useful to put in the binding for the scripts. The binding is a map of variables that are available to the script for use. Right now I have a variable called "out" which is the Session output stream. It allows things like

out.println "This is some output"

I could just use println to have it go to stdout, but that would not work when the Session explicitly sends output to a file and it also makes testing harder. Capturing stdout for the test is annoying whereas getting the output from the test session in the command under test is easy.

So now I go back the proposal and decide what is required to be available in the scripts and how to get it there.

Tuesday, January 12, 2010

Test Automation. Duh.

I haven't done too much since the holidays and I was having a hard time focusing again and I finally realized why. My testing sucked.

While embedding the GroovyScriptEngine into JMXTerm I was writing unit tests for all of the new classes and updating old test classes where I was modifying existing behavior. This was all fine, but then when I went to try out my new embedded engine with test scripts I would start JMXTerm and try running the commands that the scripts would implement. Sometimes it would work, sometimes it wouldn't, but every time it was a real pain.

I finally realized that I needed higher level functional tests that simulated me typing at the command line in addition to the class level unit tests.

There is a method on the CommandCenter class where all of the arguments typed in to the command line or passed in through stdin are processed. I not have a set of static test utility methods that can take a command line and run them through the entire application. After execution the test methods verify that the output from the script matches some regular expression and that the boolean return value of the execute() method is as expected.

This was really easy to do but it didn't dawn on my that I needed more than just class level testing to make it all work. I spend a lot of time at work selling people on automation. I guess I need to yell at me too.

I don't do any more manual testing of the command line and I love it. Developing is way more fun when there isn't one really annoying step in your workflow. Anyway, back to actual work.