AltServlet Module System

The largest problem occurred in how JavaScript files are loaded. By nature, JavaScript is not file based. It was originally intended to be loaded linearly in a web page, and little thought was given to how modules interact.

JavaScript does not have much in the way of compile-time type checking. It does not care if a function you call actually exists until you try to call it. You can pretty much load things in any order until you are ready to use them. The only place where this gets complicated is when you try to inherit classes from one another. The class you try to inherit from must be loaded first.

With those things in mind, I set out to design a module system for JavaScript that was simple to use but made it easy and intuitive to package code.

Module System Proposal : Java Packages

My initial thoughts were to create a system like Java's import/package system.

Instead of .java files, you have .js files. Instead of packages, you have modules. Each module has to be in a folder, much like packages. So the 'alt.foo' module would be a path alt/foo/, and any .js files in that folder would be scripts (as opposed to classes) of that module.

Each module is loaded in its module scope, so if you define a function Bar in the file alt/foo/Bar.js, it needs to be accessed by calling alt.foo.Bar from other scopes.

This makes it painless to define JavaScript classes and package them in modules, you simply define them as top-level classes and put them in a module folder.

Usage

Using the module system is almost identical to Java's import.

Instead of modifying Rhino to allow a custom JavaScript syntax, I decided to stick with a compatible method:

Alt.require("alt.foo.Bar");

This code states that the script Bar from the alt.foo module must be loaded at this point. This can be read as, the current script depends on alt.foo.Bar.

AltServlet will then check if the script has been loaded and up to date. If not, it will load, compile, and execute the script in its module scope. If it fails to load, compile, or execute, an exception will be thrown.

If the Bar script defined a class named Bar (this is the recommended convention), then you can access it through its module path (this is where AltServlet diverges from Java):

var bar = new alt.foo.Bar();
      

Pretty simple? As with any JavaScript, you can easily make class aliases so you do not need to write out the whole module path each time you instantiate a class:

var Bar = alt.foo.Bar;

Advanced Usage: Wildcard Loading

There are a few extra features of the module system that can be used.

In addition to including individual scripts, you can load all of the scripts in a particular module by using the wildcard script name:

Alt.require("alt.foo.*"); 

This will attempt to include all scripts in a particular module. Caution should be used when including scripts in this manner, because it does not guarantee a particular script will actually be loaded.

Like Java, this will not load scripts from sub-modules.

Advanced Usage: Cascading Dependencies

When extend classes in JavaScript, the code looks something like this:

// Foo.js 
function Foo() { ... }
// Bar.js
Alt.require("Foo");
function Bar() { ... }
Bar.prototype = new Foo();

As you can see, the Bar.js file constructs a new Foo object to create its prototype. If you were then to go and modify Foo to add a function:

// Foo.js 
function Foo() { ... }
Foo.prototype.sbu = function() { ... }

AltServlet would recognize that Foo was modified and reload it, but Bar would still be pointing at the old Foo, the Foo that is missing the sbu() function.

To solve this issue, one would generally need to restart the server, but AltServlet provides "cascading dependencies" to solve the issue.

// Bar.js
...
Alt.require("Foo", true);
...

The second (optional) argument of the Alt.require function specifies whether a file should be considered a cascading dependency.

This tells AltServlet that Bar needs to also be reloaded whenever Foo is reloaded. (If this feature were enabled by default, then every single script would be reloaded every time one script is modified.)

page last updated: Thursday, February 14, 2008

copyright © 2006 cellosoft