JavaScript is the world’s most widely deployed (and most misunderstood [Crockford 2001]) programming language. With the dawn of HTTP client support in modern web browsers (“Ajax”) and more powerful, standard rendering engines, JavaScript — once considered a mere toy language for designers and hacks — has enjoyed quite a renaissance in the last five years. Indeed, server-side JavaScript is enjoying something of a renaissance itself at the moment, due in no small part to the meteoric rise of Node. What is not as well known is that there have been powerful JavaScript server options for quite a long time, thanks to Mozilla’s excellent Java-based JavaScript engine, Rhino. Rhino is the foundation of two very mature JavaScript platforms: Narwhal and RingoJS (formerly Helma), both of which are supported by the AppEngine JavaScript SDK. We’ll be using RingoJS for this demonstration.
Additionally, as our language and framework options on the server have grown and diversified the last five years, the “cloud computing” paradigm has moved onto center stage. With cloud computing we get simpler deployment and maintenance scenarios, straightforward (somewhat) scaling to very large amounts of data, and many other advantages that are beyond the scope of this demonstration. While many cloud solutions sit at the operating system or infrastructure level (e.g. virtualized Linux sandboxes), Google AppEngine offers a virtualized platform to which stand-alone applications can be deployed, and which provides a sophisticated, scalable data storage system for applications. This platform comes in two flavors: Python and Java. AppEngine’s Java support, via Rhino, is what enables us to run our JavaScript server platform of choice on the Google AppEngine infrastructure (mostly) as-is.
For this demonstration, we’ll be deploying a trivial JavaScript application to AppEngine. Before we can do that, however, we’ll need to get our environment set up. You’ll need the following technology stack:
ant jar in the ringojs directory to build the Java portion of the project.packages directory of ringojs.I keep these repositories in a Projects folder in my home directory, but you can put them wherever you like. Also, walking through the demonstration will be simpler if you put the AppEngine Java SDK and RingoJS bin directories on your search path.
RingoJS supports the CommonJS JSGI standard, providing tools, helpers, and middleware to build simple web applications. It also supports the concept of application skeletons, which is how we’ll bootstrap our AppEngine project into existence. Navigate to the directory where you want to store your project, and run the following:
$ ringo-admin create --appengine myapp
+ Copying apps/appengine to myapp ... done
+ Copying apps/skeleton to myapp/WEB-INF/app ... done
+ Copying modules to myapp/WEB-INF/modules ... done
+ Copying jar files to myapp/WEB-INF/lib ... done
As you can see, a skeleton RingoJS project structure is now set up for you. Before the project will run in the AppEngine environment, however, we need to copy the AppEngine SDK jars into place:
$ cp <APPENGINE_HOME>/lib/user/appengine-api-*.jar myapp/WEB-INF/lib
At this point, the application doesn’t do anything, but you can see that all the plumbing works. Start up the development app server and point your browser to localhost:8080:
$ dev_appserver.sh myapp
You should see an HTML page containing the text: “It’s working!”
To get our feet wet, we’ll build a trivial web application that displays a list of sign-ups (name and e-mail address), and allows new users to sign up and be added to the list. If you look inside of myapp/WEB-INF/app/config.js you’ll see a block of code that looks like this:
exports.urls = [
['/', 'actions'],
];
Let’s add a /signups URL so that it looks like this:
exports.urls = [
['/', 'actions'],
['/signups', 'signups']
];
You can think of /signups as being a kind of route endpoint which provides a set of actions. The URL /signups/index, for example, will be handled by the index function that is exported by the signups module. Let’s create that now. Copy the following code into a new file called myapp/WEB-INF/app/signups.js:
include('ringo/webapp/response');
var db = require('google/appengine/ext/db');
var Signup = db.Model('Signup', {
name: db.StringProperty(),
email: db.StringProperty()
});
exports.index = function (req) {
return skinResponse('skins/signups.html', {
signups: Signup.all().fetch()
});
};
Apart from requiring a few modules, there are two things going on here. First, we define a Datastore model called Signup with two properties: name and email. (If you’ve used the Python AppEngine APIs, you should notice a family resemblance.) Next, we export an index action like we described above. This action uses the Ringo skins mechanism to render a list of Signups from the Datastore. We’re not going to go into skins in any detail here: for now, just paste the following code into a new file called myapp/WEB-INF/app/skins/signups.html. You should be able to figure out the basic gist of it:
<h1>Signups</h1>
<h4>Current Signups</h4>
<ul>
<% for s in <% signups %> render signup %>
</ul>
<% subskin signup %>
<li>
<% s.name %>:
<a href="mailto:<% s.email %>"><% s.email %></a>
</li>
At this point, if you restart the dev app server, you should be able to see the (empty) signup list at localhost:8080/signups/index. This has potential, but it would be even nicer to be able to sign up new people. Let’s add a form to the bottom of our page (above the <% subskin signup %>):
<h4>New Signup</h4>
<form method="POST" action="/signups/create">
<input name="name"> Name <br>
<input name="email"> E-mail <br>
<input type="submit">
</form>
And then a create action to signups.js to respond to it:
exports.create = function (req) {
new Signup({name: req.params['name'], email: req.params['email']}).put();
return redirectResponse('/signups/index');
};
This action simply puts a new Signup entity into the Datastore and redirects us back to the index page. Try it!
At this point we can see that our application is behaving correctly in our local environment. Let’s deploy it to AppEngine and see it working there. First you will need to go to appengine.google.com and sign up for the AppEngine service (if you haven’t done so already), and then you will be able to create an application. Once you’ve done that, open up myapp/WEB-INF/appengine-web.xml and change the value of the <application> element to the name of your newly created application:
<application>my-application-id</application>
We’re now ready to deploy! Just run the AppEngine SDK’s app config script: (You may need to provide your GMail address and password if it’s your first time running the updater.)
$ appcfg.sh update myapp
After the deploy is complete, you’ll be able to see your newly deployed application at my-application-id.appspot.com/signups/index.
Whether you’re taking a serious look at a server-side JavaScript solution, or just playing with fun, emerging technologies, I hope you’ve found this little tutorial useful.