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

command
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

closure("argument")


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.

@Override
public Object getVariable(String name) {
try
{
return super.getVariable(name);
} catch (MissingPropertyException e)
{
Boolean callReturn = (Boolean) closure.call(new 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

command


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.

Sunday, November 15, 2009

Its starting to work, GroovyScriptCommandFactory is live.

Now that GroovyScriptCommand is mostly working I did more work on GroovyScriptCommandFactory. I added the code to configure the script engine with the default script directories where it will look for scripts to execute. I’ll be adding the mechanisms for customizing the script directories later. With that the factory does most of what it needs to do to function as a CommandFactory to find scripts in the script directories.

The next big step was to change the PredefinedCommandFactory to look up commands in more than just the TypeMapCommandFactory. In the original code the CommandCenter, which is where the main execution happens, created a new PredefinedCommandFactory which would delegate to a TypeMapCommandFactory to create the static Command objects. I added logic to PredefinedCommandFactory to have a List instead of a single CommandFactory delegate. Each CommandFactory is added in turn and each one is checked to see if it can create the command requested when createCommand(String) is called on the PredefinedCommandFactory. The behavior of has changed slightly in that all of the CommandFactories are given the chance to create a new Command and only if all of them fail is an IllegalArgumentException thrown.

Because of this, the order that the CommandFactories are added to the list of delegates is important as it is also the order that they will be given a chance to create a Command object. Right now the static TypeMapCommandFactory is added first and checked first. This way there is no way to have a script called that is the same name as an existing static Command. I’m not sure how I feel about this. Its easy to change so I’ll see how I like it as I continue testing.

All of this works in new unit tests and I tried it on the command line. It was pretty cool to see my test scripts getting run from the interactive shell. Its starting to come together. I don’t think the script engine integration part I’m doing now is going to be all that hard. I think that designing and implementing the script API is going to be tougher, mostly because I’ll probably be changing it a lot as I start to use it myself to write scripts. No one is a tougher critic on your code than your first user.

Monday, November 9, 2009

Working Code: Running scripts from GroovyScriptCommand

Working off of the examples of the existing CommandFactories and Commands in the code, I wrote the GroovyScriptCommandFactory and the GroovyScriptCommand as a wrapper for all script commands. There are some assumptions made in the existing code that instances of Commands will be known at compile time so that information about them, such as annotations on the class, can be used for help output and command names. I'm not crazy about this as it makes doing dynamic commands much harder. I would have preferred to have a more standard abstract method or something that can be called so that I could, for example, return the description of the script from the script itself which is known at runtime when the method would be called. As it is, there are helper classes which reflect on the Command subclass and output the description from the annotation.

I think my two options here are
  1. Go with a single GroovyScriptCommand that has a lame description defined in the annotation like "To get help for this script, run it with 'help' as the only argument"
  2. Do some crazy runtime class modification to add annotations to the subclass when the script name is known and load the description from the script if it is available and return some standard "no help" message if the script does not implement a description. I'm not yet sure how the script would do this. That is left as an exercise for future me.
Right now I'm going with #1 as its quick and easy. We'll see how hard #2 is later.

I'm actually pretty happy with the GroovyScriptCommandFactory. This is the subclass of CommandFactory that is responsible for creating the instances of Command based on a string name. This is pretty easy as the string name of the command will be the name of the groovy command without the .groovy at the end. I decided to return a Command object even if the script requested doesn't exist so that the exception can be thrown when the Command.execute() method is called. Since CommandFactory.createCommand(String) does not throw exceptions I could only return null which isn't hugely helpful.

Unit tests work and I can read variables set in the script binding and write new ones which can be read by the unit tests. There's still a little more work to have the GroovyScriptCommandFactory get created on startup and be asked to create Commands. Right now there is only one hard coded CommandFactory called and it is configured by a property file. I'm not sure how flexible this needs to be or if I can just change it to create the original CommandFactory and the groovy one and call one or the other first. My gut reaction is to have the scripts override the built in commands as that's how bash works but I could easily see it going the other way as overwriting builtin commands is crazy making. Its like monkey patching in dynamic languages. Not for the faint of heart.

Monday, November 2, 2009

Steeping in the code

I've been staring at the code for a while peppering with TODO statements where changes need to be made to allow the Groovy script engine to do its thing.

During startup, a CommandCenter object is created that does most of the delegating of work. It passes commands off to the CommandFactory to get the Command object, creates an instance of the Command and then runs it returning the result.

Right now the CommandFactory is hard coded to create a PredefinedCommandFactory object. This seems to just parse a properties file which contains names of commands and their corresponding classnames. These are put in a map of name to classname and used to create another factory called TypeMapCommandFactory which creates instances of commands based on the name requested and the classname in the map, throwing exceptions if they are missing.

In PredefinedCommandFactory, the TypeMapCommandFactory is held as an instance field called "delagate". There may have been grander plans for the PredefinedCommandFactory and the CommandFactory in general as it seems overly abstract, but it does provide a great place to put in my own CommandFactory.

My CommandFactory will be able to take in a command name and look up whether a Groovy script exists with that name in the scripts directory, however it was configured. I'm not sure whether or not built in commands or scripts will take precedence during name collisions. That might even be an exception as its too weird to want to do on purpose.

I intend to leave PredefinedCommandFactory in place and change its implementation to check with both the GroovyScriptCommandFactory and the TypeMapCommandFactory to return the Command.

Next up, besides actually doing that, is to create a base GroovyScriptCommand from which the scripts will inherit.