TDIing out loud, ok SDIing as well

Ramblings on the paradigm-shift that is TDI.

Wednesday, August 29, 2007

What's in a name?

Ok, so it's not immediately obvious that the pre-defined script variable system is actually detailed in the TDI JavaDocs under the com.ibm.di.function.UserFunctions class. Or that main goes by the whimsical class name com.ibm.di.server.RS and task is com.ibm.di.server.AssemblyLine.

But this is no problem when all you have to do is ask:

task.logmsg(" system: " + system.getClass());
task.logmsg(" task: " + task.getClass());
task.logmsg(" main: " + main.getClass());

The getClass() method is available for all Java objects and so will work for any variables that reference one; However, it will not work for JavaScript types (e.g. Number, String of Boolean).

To get around this limitation we can make our own getClass() function:

function getClass( v ) {
if (typeof(v) == "object")
return v.getClass()
else
typeof(v);
}

Then you'll be on speaking terms with variables from both worlds.

Monday, August 27, 2007

Exceptional trick for initialization code

Let's say you have an AssemblyLine that will be processing thousands of entries. and you want a progress message written every 100 cycles. This will require a counter.

var entryCount = 0;

And you'll need a snippet of script to write your message.

// Increment the counter and test if it's time to write the progress message.
// I chose here to write to standard output and not the log
//
entryCount++;
if (entryCount % 100 == 0)
java.lang.System.out.println("Entries processed: " + entryCount);

Since you don't want the counter set to 0 every cycle, you need to have your initialization code outside the main loop of the AL. This leaves three choices.

1) Put the init code in a Prolog Hook (either of the AL or some component). This is a common approach, but it does make ALs a bit harder to navigate since references to the same variable are spread across components and the AssemblyLine itself.

2) Another technique is to use a Connector Loop instead of standard Feeds-Flow behavior to drive data in your AssemblyLine. Without an active Feeds section, your initialization simply code can be handled by a Script component at the start of the Flow section. Legibility is improved since you get a component, preferrably named something like "Initialization", visible at the start of your AL. Of course, the AssemblyLine is a tad more complex, and you don't get to exploit End Of Cycle behaviors (like Iterator State Persistence). Plus you still initialize variables one place and then use them someplace else.

3) That leads me to my final point where I reveal the pun in my title above: use exception handling to ensure that code is run once and only once.

Exceptions are how errors are flagged and passed around in development languages like C++, Java and JavaScript. When some piece of code gets into trouble, it sends up a flare - which is called throwing an exception in the parlance. This exception causes normal processing to stop and control to be passed back up the call stack until it is either caught, or it causes the application to abort with an "unhandled exception" message.

TDI has exception handling logic that passes control to Error Hooks, as described in the Flow Diagrams, but you can implement your own using the JavaScript try-catch statement.

try {
... try some code that may fail with an exception ...
} catch (excptn) {
... end up here if the above fails (and passed the "excptn" variable) ...
}

Going back to the initial scenario of writing progress messages, all counter-related logic can be implemented in a single Script component at the top of your Feeds section - regardless of whether you are using an Iterator in the Flow or not.

try {
// This next line fails the first time since entryCount is defined.
entryCount++;
if (entryCount % 100 == 100)
java.lang.System.out.println("Entries processed: " + entryCount);
} catch (excptn) {
// The code below is invoked when the above fails (first time only).
// Note that we must init entryCount to 1 here, instead of 0 as before.
entryCount = 1;
}

With this method you still get good readability (although your Script component should probably be named "Init and show progress") and you keep the initialization and usage of your script variable to manageable snippet.