Examples

Quickstart

Assuming you already have Java 6 installed:

  1. Download the ZIP archive.
  2. Extract it and cd to the ESXX directoy in a console.
  3. Start the built-in webserver on port 7777 by executing java -jar esxx.jar -H 7777.
  4. Point your web browser to http://localhost:7777/ and run some of the demo applications in the examples directory.
  5. Shut down ESXX by pressing Ctrl-C in the console.

If you instead have installed ESXX using one of the OS-specific packages, cd into the examples directory (usually located in /usr/share/doc/esxx/examples/ or C:\Program Files\ESXX\examples\) and start the built-in webserver on port 7777 by simply executing esxx -H 7777.

Some of the demo applications will automatically create H2 SQL databases in the current directory. They can be safely removed once ESXX has been stopped.

Examples

Here are a few tiny examples that demonstrate a few key features of ESXX. In order to be able to execute the script files from the command line, you must have installed ESXX. Otherwise, use java -jar esxx.jar -s <script.js> to run them.

load-uri.js

Let's start with a command line example. Assume you would like to fetch an URL and display the content of that URL as unparsed text. Here's one way to do it. Invoke the script with ./load-uri.js http://example.com, for instance.

#!/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("text/plain");

  out.println(data);
  return 0;
}

The URI class is truly fundamental in ESXX. It's used to read and write files, list directories, fetch and store web resources, communicate with web services, look up information in LDAP directories, query SQL databases, and more. In this example, load() is used to fetch the resource, which is then parsed by the text/plain parser and returned as a JavaScript object (here, a String). The URI class has four more methods: save(), append(), remove() and query().

last-sender.js

Haven't you always wished you could find out who last sent you an e-mail, without opening your mail program? I thought not. But let's script the Java classes in JavaMail to find out.

#!/usr/bin/env esxx-js

importClass(java.lang.System);
importPackage(javax.mail);

function main(prg, location, username, password) {
  if (arguments.length < 3) {
    System.err.println("Usage: " + prg + " <IMAP URI> <username> [<password>]");
    return 10;
  }

  password = password || System.console().readPassword("Password: ");

  var session = Session.getDefaultInstance(System.getProperties());
  var store   = session.getStore(new URLName(location));

  store.connect(username, password);

  var inbox   = store.getFolder("INBOX");
  inbox.open(Folder.READ_ONLY);

  var message = inbox.getMessage(inbox.getMessageCount());

  System.out.println("Last message was from " + message.getSender());
  return 0;
}

Execute the script with ./last-sender.js imaps://imap.gmail.com <username> and enter your password. My last mail was from a spammer — who would have thought?

wall.esxx

I started with non-web examples for two reasons: First, I wanted to demonstrate the URI class and how it can be used to access external resources. Second, it's important to point out that ESXX puts no limitations on what you can do. If the minimalistic ESXX APIs does not expose something you really need, you just script Java directly and move on to the next programming task. (And before you go home for the day, don't forget to file a feature request — or post a patch — on the Berlios project page.)

Now, however, is the time to look at an ESXX web application. Do you remember the early days of the web, when the coolest thing you could have on your home page was something called a guestbook, where your friends could write messages to you and read what others had already written? Yeah, I know … Very novel. Let's get started.

<?xml version="1.0" ?>

<esxx xmlns="http://esxx.org/1.0/">
  <handlers>
    <http method="GET"  uri="/" handler="showMessages" />
    <http method="POST" uri="/" handler="addMessage" />
    <stylesheet href="wall.xslt" />
  </handlers>
</esxx>

<?esxx
var db = new URI("jdbc:h2:mem:wall;DB_CLOSE_DELAY=-1");

if (db.query("SELECT count(*) AS cnt " + 
	     "FROM information_schema.tables " +
	     "WHERE table_name = 'GUESTBOOK'").entry.cnt == 0) {
  db.query("CREATE TABLE GUESTBOOK (name VARCHAR(32), message CLOB, date DATETIME);");
  esxx.log.info("Created table GUESTBOOK");
}

function showMessages(req) {
  return <wall>
    {db.query("SELECT name, message, date FROM GUESTBOOK LIMIT 10", { $result: "entries" })}
    <form/>
  </wall>;
}

function addMessage(req) {
  if (!req.message.name || !req.message.message) {
    return <wall>
      <error>Name and message must be non-empty.</error>
      <form>
        <name>{req.message.name}</name>
        <message>{req.message.message}</message>
      </form>
    </wall>;
  }

  db.query("INSERT INTO GUESTBOOK(name, message, date) VALUES({name}, {message}, NOW());",
	   req.message);
  esxx.log.info(req.message.name + " wrote: " + req.message.message);

  return [ESXX.Response.SEE_OTHER, {Location: req.scriptURI.valueOf().replace(/\/$/, "")} ];
}
?>

The entry point of an ESXX web application is an XML file that defines the active URI, SOAP, XSLT and error handlers. Usually, this XML file also contains a few <?esxx-include?> processing instructions that loads the actual JavaScript code into memory. In this example, we include the code directly in the XML file using <?esxx?>, but that's something you would normally never do.

In the example, we define two GET and POST handlers, showMessages() and addMessage(), and an XSLT stylesheet that will be triggered by all XML results.

The handlers can return different kinds of data, like plain text, binary data, Java image objects, or XML. When a handler returns XML data, XSLT stylesheet processing is enabled and if there is a matching stylesheet registered, the result will be transformed accordingly.

Knowing this, it's easy to understand how wall.esxx works:

  1. At application load time, the global variable db is initialized to the location of an in-memory H2 SQL database. Then, a test query is run to find out if the table GUESTBOOK exists; if not, it's created. Code in the global scope is only executed once during the application's lifetime.
  2. After that, the application begins waiting for requests. When a GET request arrives, getMessages() will return an XML document. This document includes everything that is to be presented to the user, including a list of messages fetched from the database. The <form> element signals that the stylesheet should also render an HTML form, where the user can enter a message.
  3. Once the response has been returned from getMessages(), the result is transformed into HTML by the XSLT stylesheet. The final document will contain an HTML form where new messages can be entered. Have a look at the stylesheet to see how it's done.
  4. If a message is entered and the user presses the submit button, the POST request will trigger the addMessage() handler. This function validates the form parameters and either
    1. returns an error message; or
    2. adds the message to the in-memory SQL database and redirects the user back to the GET view.

And that's all there is to that: one SQL database, one XSLT stylesheet and a few lines of JavaScript. Model, view, controller. Simple, eh? No auto-generated files, no wizards, no magic and no bloat. I find it reassuring that trivial web applications can still be trivial to write. (Needless to say, all user input is encoded appropriately, both in the generated HTML document and in the database. Try it!)