Latest ESXX Release


Wednesday, February 4, 2009

JavaScript command line applications, the ESXX way

Today, I added some documentation to the wiki about ESXX command line applications, and I though I'd post it here as well.

An ESXX command line application is one or more JavaScript files that define a main() function in the global scope. It's common to also begin the main JavaScript with a shebang, and set the executable flag, so it can easily be executed from the Unix command line. /usr/bin/env is usually used as a helper method, so it won't matter where the esxx-js program is located, as long as it's in the command search path.

#!/usr/bin/env esxx-js

... code follows ...

(Note that if the esxx-binfmt service is started, any executable JavaScript file may be executed directly from the command line shell, even if it lacks a shebang. Unfortunately, this currently only works in Linux.)

The command line application is loaded and executed like this:

  1. First, a new global scope is set up, with all the Core JavaScript, Rhino, ESXX and LiveConnect host objects present.
  2. The JavaScript file is loaded, compiled and executed with this set to the global scope.
    1. During the execution, one or more functions are defined in the global scope.
    2. esxx.include() may be called to bring in other JavaScript files into the global scope, either from the current directory or from the <esxx-root>/share directory. These files may call esxx.include() in turn; however, a single file will only be loaded and executed once — attempts to load a file that has already been loaded will be silently ignored.
  3. As a final step, ESXX invokes the main() function, with the main script file name as first argument followed by the command line arguments.

Below is an example of a very simple ESXX command line application that loads data from an URI, using the URI's default parser, and prints the result to the console.
#!/usr/bin/env esxx-js

var err = java.lang.System.err;
var out = java.lang.System.out;

function main(prg, location) {
if (!location) {
err.println("Usage: " + prg + " <location URI>");
return 10;
}

var uri = new URI(location);
var data = uri.load();

out.println(data);
return 0;
}

The script can be used for many purposes. Assuming the script is called load.js, it may be used to load a web page and convert it into XML:
[martin@elsa ~]$ ./load.js http://example.com
<html>
<head>
<title>Example Web Page</title>
</head>
<body>

...
</body>
</html>

But it can also be used as an DNS resolver, thanks to ESXX's dns: URI protocol implementation:
[martin@elsa ~]$ ./load.js dns:/example.com
<result>
<entry uri="dns:/example.com??base">

<a>208.77.188.166</a>
<ns>b.iana-servers.net.</ns>
<ns>a.iana-servers.net.</ns>
</entry>

</result>

ESXX command line applications can also be GUI applications by using Java's Swing GUI toolkit. Here's a small app that displays today's XKCD strip in a window:
#!/usr/bin/env esxx-js

with (JavaImporter(javax.swing)) {
function main() {
let atom = new Namespace("http://www.w3.org/2005/Atom");

let entry = new URI("http://xkcd.com/atom.xml").load().atom::entry[0];
let img_tag = new XML(entry.atom::summary.toString());
let image = new URI(img_tag.@src).load();

let frame = new JFrame(entry.atom::title);

frame.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE;
frame.add(new JLabel(new ImageIcon(image)));
frame.pack();
frame.locationRelativeTo = null;
frame.visible = true;

esxx.wait(this); // Wait forever
}
}

Exciting, isn't it?

No comments: