TDIing out loud, ok SDIing as well

Ramblings on the paradigm-shift that is TDI.

Wednesday, October 22, 2008

I didn't always like scripting

Not back when Johan first pulled me into Metamerge. Even though I sometimes just wanted to break free of the Rube Goldberg-esque world of AL construction and do something quick and direct, like:
if (error.getString("status") != "ok")
task.shutdown(); // Invokes AL Shutdown Request Hook
My issue with scripting was that JavaScript code is "opaque" in TDI; there is no Hook flow through it, making it harder to troubleshoot and test. Furthermore, hiding code away in Hooks doesn't help AL legibility. More importantly, new TDI users often had no previous scripting experience.

And then we added real AL debugging and everything changed.

As far as scripting skills go, trial and error is how I learn. The Debugger turns an AL into an interactive tutorial where I can try my hand at executing any series of code snippets, entered one-by-one into the JavaScript commandline (or selected from the history drop-down) and get immediate feedback in the Log Output window. I can play around with stuff until it feels comfortable. If I do something that bombs with an exception, the Debugger catches and displays it, but the running AL is unharmed

So I found myself doing more scripting...

To make my ALs easier to read and debug, I started using Script components (SCs) to define functions that I called from my Hooks. Larger chunks of code are split up into separate, clearly labeled SCs. For example, the snippet at the start of this article would have appeared in an SC called something like Stop AL On Error.

Then I fire up the Debugger, step from script block to script block (since you can't step into them...yet) and examine work and conn, inspect script variables, call any functions and just generally ensure the sanity of my AssemblyLine logic and the health of the data it's working with.

The QA of my solutions improves as well. Since the AL Debugger lets me change any value or invoke any function, it's easy to trigger and test particular parts of my code. I confess to even using the system.skipTo() call - the infamous TDI GOTO command - to move the "Execute Me Next" relay baton around in my AL.

Hand on heart: TDI's interactive, try-test-refine approach lets me sculpt solutions quickly and visually by tooling around in the Debugger. This is where I learned most of what I do about JavaScript, as well as TDI in general, and REST/APP, XML, Domino, DB2, TDS, TSRM... The list goes on.

So the first time I had to deal with Domino-specific Java types, I configured a Notes Iterator, fired up the Debugger, stepped to the After GetNext Hook and then told the Attribute value in question to tell me its Java class name:
rev = conn.getObject("$Revisions"); task.logmsg(rev.getClass().getName())
Which I then looked it up in some Domino Java Classes documentation that Ken Lin pointed me at (thanks again, Ken). I also used system.dumpJavaClass(rev.getClass().getName()) to quickly get the list of publicly available methods:
[1:29:46 PM CEDT] E: system.dumpJavaClass(work.getObject("$Revisions".getClass().getName()) -> true
CTGDIS827I Class name: lotus.domino.cso.DateTime.
getDateTimeData ();
setJavaDate (java.util.Date);
setJavaDate (java.util.Calendar);
makeRDate ();
doAdjust (int, int, boolean);
getRDateTime ();
markInvalid ();
getParent ();
toJavaDate ();
...

Bingo! I could create a java.util.Date object by simply calling the Domino DateTime's toJavaDate() method.

Then I asked the JDBC Connector to query the schema of the DB2 table, learning that the target column for this value was handled as java.sql.Timestamp, and I could easily create one of those like this:
new java.sql.Timestamp(rev.toJavaDate().getTime())
Armed with Google in the one hand and my Norwegian Army Knife in the other, I happily whittled away at the solution, learning and correcting its design as I go.

The same goes for figuring out how to implement a REST-based web service, or scraping web pages for information, or provisioning accounts in Domino, Connections or Content Manager.

Scripted components, e.g. Connectors, Functions or Parsers, are trickier to debug since these execute in their own Script engines- but not impossible. Even though the Debugger gives you full access to the Script environment of the AssemblyLine itself, it can't access those of your scripted components. In order to debug their code, you need to bring it into the AL's environment, preferably as a series of SCs.



Here's an example of one such AL.

I make sure my functions get defined first, just in case I have initialization code that makes use of them. Notice how my Test script is empty. That's because I do my testing interactively from the Debugger. I just use Test as a spot to set my breakpoint.





In this screenshot I am exercising the getNextEntry() function of Iterator Mode. Of course, selectEntries() has already been called by the Variables & Init SC. On the same JavaScript commandline I also do a dumpEntry().


e = getNextEntry(); task.dumpEntry(e);
You'd be surprise how much code you can squeeze into a single line :).

For example, I remember one project where an input data source was giving us over 150 Attributes. I used this one-liner to get a sorted list:
atts=work.getAttributeNames(); java.util.Arrays.sort(atts); for (a in atts) task.logmsg(a)
TDI jockey Jon Elwood is currently building an AJAX interace to launch and monitor the progress of his AssemblyLines. You can imagine how handy the Debugger is for examining the incoming HTTP requests and interactively testing out return snippets of XHTML (or XML/JSON) by simply setting the value of the http.body Attribute before End-of-Flow. Jon will be publishing a HowTo write-up on this that I am looking forward to.

So I guess that wraps up what has turned into an Ode to the AL Debugger. Just felt the need to share :)