dcsimg

Rails vs. Grails vs. Helma: The JVM Web Framework Smackdown, Part 2

It's the ultimate JVM Web Framework Smackdown! Three will enter the cage. Only one will exit the victor. Will it be JRuby on Rails? Groovy on Grails? Or Helma?

Helma: JavaScript on the Server

The final contender is the Helma Object Publisher, more commonly referred to as Helma. You probably have not heard of Helma before. It has received the least amount of press of the three frameworks, and although it has a following in Europe, it has received little notice in the States.

But there are a few reasons you should give it some consideration. It uses Rhino JavaScript, the oldest language on the JVM besides Java itself. Rhino was created by Netscape and later inherited by the Mozilla Foundation. It is backed by Google, and it is the default scripting language implementation available in Java 6. It offers strong performance and a rich set of utilities, all of which Helma leverages beautifully. Furthermore, Helma takes an unusual approach to organization. For a different perspective, if nothing else, it is worth a look. The Helma implementation of PenguinMusic can be downloaded here

Getting started

Helma was the easiest to get up and running. Once you download the package, run ./start.sh (or start.bat if you are a Windows user), and navigate to http://localhost:8080/ to find a page with links to various tools, documentation, and the Helma website.

Lightning fast startup was one of the smartest moves by the Helma developers. They make it as easy to get started as possible. It is not that Helma’s documentation is any better than that of Rails or Grails; it is simply much more prominent. On the other hand, Helma’s developers could neaten the process of bootstrapping an application. While it is very easy to create a new project, creating a new project with a clean organization takes a couple of extra steps.

First, edit apps.properties. It is enough to just add the name of the application. Unfortunately, this makes it difficult to know where to put images, CSS stylesheets, SQL scripts, and so on. You are better off copying the setup of the “welcome” application included with Helma. The following configuration creates the new application, penguinMusic, and automatically create the directories that you need:

# List of applications to start.
# More information about this file is available at
# http://helma.org/docs/guide/properties/apps.properties/ 

penguinMusic
penguinMusic.repository.0 = apps/penguinMusic/code/
penguinMusic.repository.1 = modules/helmaTools.zip
penguinMusic.static = apps/penguinMusic/static
penguinMusic.staticMountpoint = /penguinMusic/static
  ...

It also adds helmaTools.zip, which allows you to use the Inspector (covered later). Helma comes bundled with a number of other modules. If you need to use any, the syntax to add them is the same.

An Object-Oriented Framework

The biggest challenge learning Helma is shifting your mode of thinking—not entirely different from first learning JavaScript’s prototype-based object system. Most frameworks are written in object-oriented languages; Helma is an object-oriented framework. It is a subtle, but critical distinction. With Helma, you build one massive object that is the web application. While confusing at first, the final organization seems very intuitive.

Rails and Grails are both organized with separate directories for models, views, and controllers. Helma projects are organized by prototype. Consider the structure of the app directory from a typical Rails implementation:

app/
  controllers/
  helpers/
  models/
  views/
    albums/
    artists/
    comments/
    genres/
    layouts/
    misc/
    songs/
    users/

The organization for Grails is largely the same as Rails. In contrast, here is the structure for the code directory of the PenguinMusic Helma implementation:

code/
  Album/
  Artist/
  Comment/
  Genre/
  Global/
  Root/
  Song/
  User/
  app.properties
  db.properties

A Helma application is organized around its objects. The Global and Root directories deserve some special mention. Global is for utilities that do not correlate to any object. For example, you might want to have the “featured artists” rail on multiple pages of our application. This requires a macro (more detail later).

/* Saved in Global/macros.js */
function featuredArtists_macro(param) {
  var featured = ["hipcitycruz", "namelyus", "markgrowden"];
  var str = "";
  for (var i in featured) {
    var album = root.albums.get(featured[i]);
    if (album) { str += album.renderSkinAsString("albumLink"); }
  }
  return str;
}

It is less obvious from the documentation what Root represents, but it is the object that is the web application. The view and action code is contained in Root, unless it corresponds to a specific object instance.

HopObjects

HopObjects (Helma Object Publisher Objects) roughly correspond with models in the MVC pattern. Unfortunately, Helma does not leverage the “convention over configuration” strategy, so objects must be manually wired to database tables.

There is no tool for generating the database, so you must resort to SQL. If you are not a fan of SQL, this may be a negative for you. (You can find a helpful SQL script here.)

After you create the database, you must configure Helma to interact with it. First, the proper JDBC driver should be downloaded to Helma’s lib/ext/ directory. (any libraries copied here are available for all of your Helma applications). Next, create a db.properties file in your application’s code directory. This version assumes that the database is named linmagMusic.

myDataSource.url      = jdbc:mysql://localhost/linmagMusic
myDataSource.driver   = org.gjt.mm.mysql.Driver
myDataSource.user     = root
myDataSource.password =

To continue, the prototypes must be mapped to different database tables. This is, unfortunately, one of Helma’s weaker areas. While the configuration process is not particularly onerous, after Rails and Grails, it feels like drudgery.

Here is the sample configuration for albums, stored in Album/type.properties.

_db         = myDataSource
_table      = ALBUM
_id         = ID
_name       = PUBLIC_ID

artistID = ARTIST_ID
genreID = GENRE_ID
title = TITLE
url = URL
albumCoverURL = ALBUM_COVER_URL
albumCoverPath = ALBUM_COVER_PATH
price = PRICE
blurb = BLURB
publicID = PUBLIC_ID

songs = collection(Song)
songs.filter = SONG.ALBUM_ID = ${ID}
songs.order = TRACK_NUM

artist = object (Artist)
artist.local = ARTIST_ID
artist.foreign = ID

comments = collection(Comment)
comments.filter = COMMENT.ALBUM_ID = ${ID}
comments.order = POST_DATE desc

Most of this is straightforward. Object field names are mapped to the corresponding table field names. The data source, the table name, and the primary ID are specified.

At the top of the file, there is a _name property. This allows URL paths like “albums/hipcitycruz/view”, rather than the slightly uglier “albums/7/view”. This also has details about the relationships between the objects: An album has a collection of songs and comments (“has many” in G/Rails terminology) and is associated with an artist object ( “belongs to”).

There can also be a functions.js file for any model-related code that you may have.

So far, this may seem like a slightly backwards version of Rails. Helma’s different style begins to show itself with the Root configuration. Root is an object like any other, and it needs its own type.properties. While it does not correspond with any given table, it needs to know what objects are available.

songs = collection(Song)
songs.order = NUM_DOWNLOADS desc

albums = collection(Album)
albums.accessname = PUBLIC_ID

artists = collection(Artist)

genres = collection(Genre)
genres.accessname = CODE

users = collection(User)
users.accessname = USERNAME

Not every prototype needs a collection here; in fact, comments have been omitted. For the collections that are defined, you can specify both the order and the field to use to search for specific instances.

This approach is a little more limited. Whsile it works for most cases, there are times when it is nice to search by an alternate field. Rails’sfind_by_* methods offer a distinct advantage over Helma here.

Skins

This is where things begin to look strange. In Rails and Grails, the model/domain classes don’t do very much. Their role is largely limited to interacting with the database. In contrast, HopObjects are actively involved in all parts of the system. Skins, the Helma view layer, are associated with some corresponding prototype. In Rails or Grails, you iterate over a collection and provide details on how it should be displayed. With Helma, you instead delegate the details of rendering each item in a collection to the object itself.

As an example, let’s take a look at some of the skins involved in rendering the same “most popular song” page. First is Root/main.skin. Note that, except for the <% ... %> bits, the skins are just straight HTML.

<div id="most_popular">
  <div id='genres'>
  <% response.genreLinks %>
  <div class="genre-link<% response.extraStyle %>"><a href="<% rootPath %>">All Genres</a></div>
  </div>
  <h1>Most Popular</h1>
  <% response.pageLinks %>
  <table border="0" cellspacing="5" cellpadding="5">
    <th>Song</th><th>Band</th><th>Album</th>
    <% response.songList %>
  </table>
  <hr />
  <% response.pageLinks %>
</div>

There are no control structures in this view. Values have been stored in a response object. We’ll need to render the individual rows of the table. Since each row corresponds to a song, we will store this in Song/songLink.skin:

<tr class="<% this.rowClass %>">
  <td><a href="<% this.playlistURL %>"><% this.name %></a></td>
  <td><em><a href="<% this.albumURL %>"><% this.artistName %></a></em></td>
  <td><strong><a href="<% this.albumView %>"><% this.albumTitle %></a></strong></td>
</tr>

In this skin, all the values come from the given song object. Some, like this.name, refer directly to object fields. Others, like this.artistName refer to macros, or Javascript functions, that can be called from within the skin files. Typically, these are stored in a macros.js file within the prototype’s directory. Here are the macros for Song:

  function artistName_macro(param) {
    return this.album.artist.name;
  }

  function albumTitle_macro(param) {
    return this.album.title;
  }

  function albumID_macro(param) {
    return this.album.publicID;
  }

  function albumURL_macro(param) {
    return this.album.URL;
  }

  function albumView_macro(param) {
    return this.album.href('view');
  }

You might note that many of these macros seem unnecessary. Helma’s views are, at times, a little overly-disciplined. It would be much more pleasant to refer to this.album.artist.name directly within the view. This is a definite hassle of working with Helma’s views.

Helma has no notion of layouts. Or better said, the nesting of views is such a fundamental concept in Helma that layouts do not really merit any special term. Here is Root/layout.skin, which corresponds to the layouts used in Rails and Grails.

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>Welcome to PenguinMusic</title>
  <link href="<% rootPath %>static/css/main.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="outer">
  <div id="header">
    <div id="logo">
      <img src="<% rootPath %>static/images/logo.png"
           alt="PenguinMusic, your source for Free* Music" />
    </div>
    <img src="<% rootPath %>static/images/linuxMagLogo.jpg"
         alt="MobileMusic -- your home for music on the go!" />
    <div id="toolbar">
        <a href='<% rootPath %>'>Home</a>
      | <% loginWidget %> 
      | <a href='<% registerAction %>'>Register</a>
      | <a href='<% rootPath %>faq'>FAQ</a>
    </div>
  </div>
  <hr />
  <span id='message'><% response.message %></span>
  <% leftRail %>
  <div id="body">
    <% response.body %>
  </div>
</div>
</body>
</html>

Now it’s time to stitch the views together. This is done with Helma actions.

Actions

There are actually two different approaches to modeling actions in Helma. One is to create a controller.js file and add in functions with names corresponding to the various actions. While this might be more familiar to Rails/Grails developers, it is used less often within Helma. Instead, Helma designers generally favor creating separate .hac files corresponding to each page.

Helma’s .hac files are simply JavaScript files that are executed when a page is loaded. Unlike the other frameworks, much of the programming logic that tends to seep into views ends up here instead. Here is Root/main.hac:

res.data.leftRail = this.renderSkinAsString("featuredArtists");

var genreID = null;
if (req.data.genre) {
  genreID = root.genres.get(req.data.genre)._id;
}
else {
  res.data.extraStyle = ' genre-selected';
}

var genreStr = "";
for (var i=0; i<root.genres.size(); i++) {
  var genre = root.genres.get(i);
  genre.extraStyle = "";
  if (genre._id == genreID) {
    genre.extraStyle = ' genre-selected';
  }
  genreStr += genre.renderSkinAsString("link");
}
res.data.genreLinks = genreStr;

var songs = [];
for (var i=0; i<root.songs.size(); i++) {
  var song = root.songs.get(i);
  if (!genreID || song.album.genreID == genreID) { songs.push(song); }
}

var songsAndLinks = this.renderSongs(songs, req.data);
res.data.songList = songsAndLinks[0];
res.data.pageLinks = songsAndLinks[1];
res.data.body = this.renderSkinAsString("main");

this.renderSkin('layout');

This looks noticeably more complex than the controller code of the previous implementations. However, it includes the equivalent code of all of the scriptlets and tag libraries from the other two.

The res.data object corresponds to the response object from the templates. The renderSkinAsString method is used to fill in these holes with the proper sub-templates. (Note that a few functions are missing — these are available in the sample code download.)

Helma’s actions are also organized by its objects. For example, consider the albumView_macro from the previous section:

  ...
function albumView_macro(param) {
  return this.album.href('view');
}

This is a fairly nice way to organize the actions that corresponded to a specific object. In contrast, consider the Rails’sequivalent:

link_to song.album.title, :controller=>"albums", :action=>"display", :id=>song.album

One Gripe with the Object-Centric Approach

Helma’s organization does lead to some weird cases. For instance, consider registering a new user. This should obviously be an action placed under the User prototype. Right? Wrong. Every User action must match up to an existing user. Therefore the registerUser action must be stored in Root. As a result, this directory can feel like a random grab-bag.

In theory, you could avoid this issue by creating a default object and cloning it. This would fit nicely with the cloning-based approach of prototype-based object systems, but it does not seem to be the standard for Helma.

Forms

Helma does not give you any great help with forms, for better or for worse. Here is the skin for the login widget, which is nearly straight HTML:

<div id="login">
  <form action="<% loginAction %>" method="POST">
    <label>Username</label>
    <input type="text" name="loginUsername" class="input-login" />
    <br />
    <label>Password</label>
    <input type="password" name="loginPassword" class="input-login" />
    <br />
    <br />
    <label>&nbsp;</label>
    <input type="submit" value="login" />
  </form>
  <br />
  <a href="<% registerAction %>">Register for an account</a>
</div>

For the actions, add macros to Global/macros.js. (While not strictly required, it does look a little cleaner this way):

function loginAction_macro(param) {
  return root.href('login');
}

function registerAction_macro(param) {
  return root.href('register');
}

Posted data is stored in req.data. Here is login.hac:

if (req.isPost()) {
  var uname = req.data.loginUsername;
  var pword = md5sum("" + uname + req.data.loginPassword);

  var usr = root.users.get(uname);
  if (usr && usr.password == pword) {
    session.login(usr);
    res.message = "Welcome back to PenguinMusic, " + usr.username;
  }
  else {
    res.message = "Sorry, we do not have that username/password combination.";
    res.redirect(root.href());
  }
  
  if (session.data.postLoginPage) {
    var page = session.data.postLoginPage;
    delete session.data.postLoginPage;
    res.redirect(page);
  }
  else {
    res.redirect(root.href());
  }
}

res.data.body = this.renderSkinAsString("login");
this.renderSkin("layout");

Helma features a res.message field that largely works like the flash in Rails. Generally this is just a string, rather than a full hash, but this is up to you. As with flash, you can use it to respond with error messages.

Integrating with Java

Once again, let’s turn to Java for the MD5 hash function. Create Global/functions.js to store this:

importClass(java.security.MessageDigest);
importClass(java.math.BigInteger);

function md5sum(s) {

  s = new java.lang.String(s);
  var m = MessageDigest.getInstance("MD5");
  m.update(s.getBytes(), 0, s.length());
  var hash = java.lang.String.format("%32s", new BigInteger(1, m.digest()).toString(16));
  return hash.replace(' ', '0');
}

Like with JRuby, Rhino must explicitly specify that something is a Java String, though this is not too major of a hassle. Now, the function needs to be tested. Um… except there is no testing framework built into Helma.

Batteries Not Included

Unfortunately, Helma is weak in some areas. While it includes a respectable set of built-in libraries, the code collections are still small compared to those offered by Rails and Grails. There is no built-in tool for pagination, nor any plugin. There is no tool for deploying Helma apps as WAR files. Setting up the database requires some degree of hand-wiring. There is no built-in tool for scaffolding, dynamic or otherwise.

On the other hand, Helma does come with a built-in administrative tool. It can be used to monitor all different parts of your application, or even multiple applications running on the same server.

It also features the Inspector, which is enabled by including helmaTools.zip in the list of repositories. While it might be a little too much for your average product-manager to use, it is a fully-functional tool that you can have available as soon as you launch. Like with Grails’s dynamic scaffolding, adding toString methods to your different HopObjects can greatly improve the usability of this tool. Here is a picture of the inspector:

Helma has a respectable set of tools available, but there are areas where you are forced to roll your own.

Final Thoughts on Helma

Although less established than either Rails or Grails, developing an application in Helma was a pleasure. The organization, while unusual, was also very powerful and will challenge your assumptions about how web development should be done. Although Helma could use some additional features, you should still give it some serious consideration.

The Good

  • Object-focused organization
  • Very clean views

The Bad

  • A little feature-poor
  • More hand-wiring of objects to database tables required

Conclusions

These frameworks are by no means the only ones available. Jython is nearly as well-established as Rhino JavaScript, and it could be used as the implementation for Django or TurboGears. Clojure has Webjure and Compojure; both are still in nascent form, but might be worth consideration if you are a Lisp fan.

If you like Rhino, but are not impressed with Helma, Phobos is another option. Also, Google is rumored to have its own Rhino web framework in the works, though to date no one outside of Google has actually seen it.

Lift has gained a great deal of attention from Scala developers. Although Scala is not a scripting language, Lift has been compared to Rails.

So which framework should you use for your new project?

JRuby on Rails will be the easiest adjustment for Rails developers, plus it gives access to Ruby’s libraries as well as Java’s. It seems to be the most established and battle-tested of the three frameworks covered here.

Grails is a little more bleeding-edge, but it features the nice organization you expect of Rails. Furthermore, it integrates with Java and leverages Java libraries better than the rest.

Helma is a little more unusual in its organization. While its libraries are decent, they pale compared to Rails and Grails. Still, it was the most fun to develop in. If you would like to see a different approach to web development, give it a look.

Fatal error: Call to undefined function aa_author_bios() in /opt/apache/dms/b2b/linux-mag.com/site/www/htdocs/wp-content/themes/linuxmag/single.php on line 62