Helma Logo
main list history
previous version  overview  next version

Version 1 by zumbrunn on 20. February 2009, 16:17

This tutorial walks you through setting up Helma and implementing a simple addressbook application that stores its data inside a relational database.


=== Preparing Helma

I assume you already managed to install a <% shortcut name="download" text="recent version of Helma" %> and that it is now <a href="/download/installation/">up and running</a>.

First of all, you have to tell Helma about your new application. Add a line with a more or less meaningful name (e.g. <tt>addressbook</tt>) to the file <tt>apps.properties</tt> in the Helma installation directory (you might find some lines containing other application names already):

<tt>apps.properties:</tt>
<pre># List of apps to start.

base
base.mountpoint = /

manage

<b>addressbook</b></pre>

Helma will now create a new folder in the <tt>apps</tt> directory called <tt>addressbook</tt>, inside of which you will later place the Javascript files and skins that define your application.

The application's name is also the first part of the URL path if you want to access the application from a browser. If Helma is running with the default settings, you should be able to request the URL <a href="http://localhost:8080/addressbook/">http://localhost:8080/addressbook/</a>.

However, there is nothing there yet and you only will get an error message stating "Error in application 'addressbook': Action not found". Since the request didn't reference a particular HopObject and/or specify a specific action, Helma was looking for the "main" action of the "root" HopObject, which you have not yet specified.

This main action will later contain the code that generates a page listing your address book entries. For now, you may just create the following main action, in order to verify that your otherwise empty addressbook application is configured correctly.

apps/addressbook/Root/main.hac:
<pre>res.data.title = "Helma Address Book";
renderSkin("html");
</pre>

This action attempts to render a skin named "html", which you  create inside the "Global" directory:

<tt>apps/addressbook/Global/html.skin:</tt>
<pre>&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"&gt;
&lt;head&gt;
&nbsp;&nbsp;&nbsp;&lt;title&gt;&lt;% response.title %&gt;&lt;/title&gt;
&nbsp;&nbsp;&nbsp;&lt;meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"&gt;
&lt;/head&gt;
&lt;body bgcolor="white"&gt;

Under development: &lt;% response.title %&gt;

&lt;/body&gt;
&lt;/html&gt;</pre>

Instead of the above mentioned error message, you should now receive the following response.

<tt>Under development: Helma Address Book</tt>


=== Database Connection

While Helma features an embedded object-oriented database, where HopObjects are persisted when they are not mapped to an external database, Helma is able to connect to almost any database system in the world. Well, at least if there is a so-called JDBC driver available for it. See the short guide about <a href="/docs/howtos/mysql/">using MySQL</a>, which should help you preparing the freely available MySQL server for a connection with Helma.

To wire the MySQL database to your Helma application, you need a file called <tt>db.properties</tt> in your application root (ie. the folder <tt>addressbook</tt> according to my example). Use your favorite text editor and enter the following lines:

<tt>apps/addressbook/db.properties:</tt>
<pre>myDataSource.url      = jdbc:mysql://localhost/addressbook
myDataSource.driver  = org.gjt.mm.mysql.Driver
myDataSource.user    = helma
myDataSource.password = password</pre>

This defines <tt>myDataSource</tt> as a connector to the MySQL database called <tt>addressbook</tt> for the user <tt>helma</tt> identified by the password <tt>password</tt> &#150; which you should replace with your password as chosen when setting up the MySQL installation (you certainly can use a different user/password combination as well, as long as it allows to connect to the MySQL server).

If you want to use a different database system or if you gave a different name to your database, that's no problem for Helma at all. Simply replace your JDBC driver or fill in the appropriate name. And certainly, you also can connect to a database server on another machine by arranging the URL, though you might have to set the database access privileges differently.

If you have done everything alright, you should restart either Helma or at least the <tt>addressbook</tt> application.

To restart an application visit the manage-interface at <a href="http://localhost:8080/manage/">http://localhost:8080/manage/</a>. If you haven't visited it up to now you'll get asked to set an initial password. Unless you are experimenting with a local Helma installation, you will probably have to edit the server.properties file and add your (local) IP address to the allowAdmin property.


=== Object-Relational Mapping

The goal is now, to "wire" the relational tables in the MySQL database to the object-oriented structure of Helma. While MySQL (and any other relational database system) structures data in tabular form, each row representing a data set, in Helma each table row becomes a HopObject using the columns as named subnodes, also called properties.

To achieve this, you will now create a prototype such HopObjects can be adapted from. While this sounds complex in theory, in practice you do this simply by adding a new directory to your application (e.g. by issueing <tt>mkdir apps/addressbook/Person</tt>).
<pre>
+----+-----------+-----------+---------------------+
| id | firstname | lastname  | email              |
+====+===========+===========+=====================+
|  1 | Hannes    | Wallnoefer | hannes@helma.org    |
+----+-----------+-----------+---------------------+
|  2 | Robert    | Gaggl    | robert.gaggl@orf.at |
+----+-----------+-----------+---------------------+
|  3 | Tobi      | Schaefer  | tobi@helma.org      |
+----+-----------+-----------+---------------------+
fig.1 A relational database table.

+-------------+
| HopObject 1 |
+===========+=+----------------+
| FIRSTNAME | Hannes          |
+-----------+------------------+
| LASTNAME  | Wallnoefer        |
+-----------+------------------+
| EMAIL    | hannes@helma.org |
+-----------+------------------+

+-------------+
| HopObject 2 |
+===========+=+-------------------+
| FIRSTNAME | Robert              |
+-----------+---------------------+
| LASTNAME  | Gaggl              |
+-----------+---------------------+
| EMAIL    | robert.gaggl@orf.at |
+-----------+---------------------+

+-------------+
| HopObject 3 |
+===========+=+--------------+
| FIRSTNAME | Tobi          |
+-----------+----------------+
| LASTNAME  | Schaefer        |
+-----------+----------------+
| EMAIL    | tobi@helma.org |
+-----------+----------------+
fig.2 Three HopObjects representing the three
rows of the table.</pre>
These so-called type mappings are set-up in the file <tt>type.properties</tt> for each HopObject. We create such a file in the next step.


=== "Wiring" The Prototype

A <tt>type.properties</tt> file that fits with the example MySQL database <tt>addressbook</tt> looks like this:

<tt>apps/addressbook/Person/type.properties:</tt>
<pre>_db        = myDataSource
_table      = PERSON
_id        = ID

firstname  = FIRSTNAME
lastname    = LASTNAME
email      = EMAIL
</pre>

What exactly happens here? The two lines at the beginning tell Helma to map the prototype <tt>Person</tt> and all its HopObjects derived from it to the table <tt>PERSON</tt> (please note the different case!) found via the database connection established with <tt>myDataSource</tt> &#150; the MySQL connector as defined before.

So, <tt>_db</tt> defines the database connection (or: data source) and <tt>_table</tt> the database table to be used. What's missing are the details of how the relational table columns should be mapped to the object-oriented database.

Most important is the mapping of the table's primary key index to the object-oriented database index: this key is necessary to provide a unique identifier for both, each entry in the relational database and each HopObject. And in consequence, <tt>_id</tt> maps the two to each other.

Btw. <tt>_db</tt>, <tt>_table</tt> and <tt>_id</tt> are internal Helma properties. That's why they start with an underscore for not getting in the way of your custom properties. However, they are mandatory for each <tt>type.properties</tt> file.

The five following lines assign each column of the relational table to a property of the HopObject. E.g. the data that is contained in the column <tt>FIRSTNAME</tt> of the table, becomes the value of the property <tt>firstname</tt> of the HopObject, the column <tt>LASTNAME</tt> is being mapped to the property <tt>lastname</tt> and so on.

A recommended naming convention is to use lowercase (resp. mixed case) letters for HopObject properties and uppercase letters for database columns resp. table names.

So, you achieved to represent the figure of HopObjects from the prior step with a few lines in <tt>type.properties</tt>. Save the <tt>type.properties</tt> in the folder of the corresponding HopObject - in this case in the <tt>Person</tt> folder.

To generate the corresponding table in your database, you should be able to use an SQL statement such as the following.

<pre>
CREATE TABLE `PERSON` (
`ID` int(11) NOT NULL default '0',
`FIRSTNAME` varchar(128) NOT NULL default '',
`LASTNAME` varchar(128) NOT NULL default '',
`EMAIL` varchar(128) NOT NULL default '',
PRIMARY KEY (`ID`)
);
</pre>

As a side remark, be aware of what names to choose for your HopObject's properties. Just as the database columns can be made up of any name as long as they do not conflict with MySQL keywords, you are restricted to use property names that do not represent reserved HopObject or ECMAScript keywords. A look in this documentation's <a href="http://helma.server-side-javascript.org/reference/">reference</a> section might help if you are in doubt.


=== Attaching HopObjects

We now successfully established a database connection from Helma to the MySQL server and mapped the relational table data to HopObject properties. But still, these derived HopObjects are freely floating around in Helma space. There is no possibility to access  HopObjects, yet. Helma even does not know about them. We need a HopObject that already is accessible, that is known by Helma. That's what the <tt>Root</tt> HopObject of an application  is for.

One could say the <tt>Root</tt> HopObject is the default place for Helma to go: "go for the root", that's what Helma can do very good. When you enter the base URL of any application, Helma is going for <tt>Root</tt>, is retrieving data from the <tt>Root</tt> HopObject first.

And as any HopObject has its prototype, there is also one for <tt>Root</tt>, ie. the directory called &#150; you guessed it &#150; <tt>Root</tt>.

To attach our <tt>Person</tt> HopObjects onto <tt>Root</tt>, we have to create yet another file called <tt>type.properties</tt>, this time for the <tt>Root</tt> prototype. It contains the following lines and goes into the &#150; you guessed it again &#150; <tt>Root</tt> folder.

<tt>apps/addressbook/Root/type.properties:</tt>
<pre>_children            = collection(Person)
_children.accessname = ID
_children.order      = LASTNAME asc</pre>

You can read this assignment the way "attach all HopObjects of the prototype <tt>Person</tt> as subnodes, indexed by the column <tt>ID</tt> of the corresponding relational table PERSON, and sorted ascending by the values in column LASTNAME".

And because we save this document in <tt>Root</tt> they will be attached to the HopObject <tt>Root</tt> (you are right when you assume that you could do this with any other HopObject simply by creating such <tt>type.properties</tt> for its prototype &#150; in fact, we have done it before with the <tt>Person</tt> prototype).


=== Shaping The Output

Because the <tt>Person</tt> HopObjects are now attached to <tt>Root</tt>, we just need a simple interface to access them and make them display in the browser.

Here's a simple XHTML layout we can use:

<tt>apps/addressbook/Global/html.skin:</tt>
<pre>&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"&gt;
&lt;head&gt;
&nbsp;&nbsp;&nbsp;&lt;title&gt;&lt;% response.title %&gt;&lt;/title&gt;
&nbsp;&nbsp;&nbsp;&lt;meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"&gt;
&lt;/head&gt;
&lt;body bgcolor="white"&gt;

&lt;% response.body %&gt;

&lt;/body&gt;
&lt;/html&gt;</pre>

Instead of defining a generic title, I am using a placeholder <tt>&lt;% response.title %&gt;</tt>, just as the whole text body is substituted by <tt>&lt;% response.body %&gt;</tt>. Both placeholders will be replaced by "real" content, later.

Helma calls such placeholder structures "macros", the files fitted with such macros are called "skins", resp. "skin files". Skin files are behaving like templates, except that you absolutely cannot evaluate any scripting code in those. And that's again what macros are for. Macros are replaced by either the result of a function call or, like in this case, by the value of an object's property.

This is one of Helma's big virtues: the separation of data and presentation. Because it is a good idea to avoid putting layout elements into your code just as it is a good idea to avoid including scripting code in your layout. Helma's macro and skin concepts help you to prevent these big no-nos and if you follow this path your web applications will become more flexible and easier to deploy, even more beautiful.

To see how the two macros will work here, save this skin as <tt>html.skin</tt> in the directory <tt>addressbook/Global</tt>.


=== Improvements

Helma's skin and macro features allow you to easily create layouts following the same hierarchical structure the represented data was built of.

We will now create another skin for the <tt>Person</tt> prototype to be used by all <tt>Person</tt> HopObjects in consequence:

<tt>apps/addressbook/Person/link.skin:</tt>
<pre>&lt;a href="mailto:&lt;% this.email %&gt;"&gt;&lt;% this.firstname %&gt; &lt;% this.lastname %&gt;&lt;/a&gt;&lt;br /&gt;</pre>

I think the three macros in this skin are pretty self-explanatory. They all refer to the current HopObject by <tt>this</tt> (ie. <tt>this</tt> will be evaluated to a <tt>Person</tt> HopObject at runtime) and three of the five custom properties we defined for it. Each of them will be replaced by the corresponding HopObject's property value.

Save this skin as <tt>link.skin</tt> in the <tt>Person</tt> directory.

The code for actions performed on an object is stored in <tt>hac</tt> files which are just text-files with a senseful name and the file-extension <tt>hac</tt> - for instance <tt>edit.hac</tt>.

The default action for an object is stored in <tt>main.hac</tt>

We will now define the default action for the application's <tt>Root</tt> object and change the file called <tt>main.hac</tt> in the <tt>Root</tt> folder to contain code that looks like this:

<tt>apps/addressbook/Root/main.hac:</tt>
<pre>
var str = "";
for (var i=0; i&lt; root.size(); i++) {
&nbsp;&nbsp;&nbsp;var person = root.get(i);
&nbsp;&nbsp;&nbsp;str += person.renderSkinAsString("link");
}

res.data.title = "Helma Address Book";
res.data.body = str;
renderSkin("html");
</pre>

If everything is done right, Helma will now render the skin <tt>Person/link.skin</tt> for each <tt>person</tt> HopObject using the corresponding object data. And so your <a href="http://localhost:8080/addressbook/">browser display</a> should look much more beautiful:

<tt><a href="mailto:xxxATxxxDOTorg">Hannes Wallnoefer</a>
<a href="mailto:xxxATxxxDOTorg">Robert Gaggl</a>
<a href="mailto:xxxATxxxDOTorg">Tobi Schaefer</a></tt>

See how easy Helma skins and macro work? And in effect, you create very flexible and modular web applications. And you even can use your own custom macros &#150; just continue reading...


=== Custom Macros

The way we filled the MySQL database with some data is quite unefficient. Why not do it with Helma?

As a first step, we enable editing the database entries and add an appropriate link to the skin file <tt>Person/link.skin</tt> (in a later step we also will make it possible to create new entries):

<pre>&lt;a href="mailto:&lt;% this.email %&gt;"&gt;&lt;% this.firstname %&gt; &lt;% this.lastname %&gt;&lt;/a&gt;
&lt;small&gt;&lt;a href="&lt;% this.href action="edit" %&gt;"&gt;edit&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;</pre>

Other than the first three macros, <tt>this.href</tt> does not refer to a property of a <tt>person</tt> HopObject. It is a custom macro that needs a macro handler, ie. a function to work:

<tt>apps/addressbook/Person/macros.js:</tt>
<pre>function href_macro(param) {
&nbsp;&nbsp;return(this.href(param.action));
}</pre>

Enter the above lines in a new file and save this as <tt>macros.js</tt> in the <tt>Person</tt> directory.

Now let's put it altogether:

Helma transforms the macro structure <tt>&lt;% this.href action="edit" %&gt;</tt> into a function call of <tt>this.href_macro(param)</tt> with <tt>param</tt> being a generic object containing the property <tt>action</tt> (along with its value).

That means <tt>param.action</tt> contains the string <tt>"edit"</tt> as it was assigned in the macro structure. The function <tt>href_macro(param)</tt> then returns the URL of the actual <tt>person</tt> HopObject plus <tt>"edit"</tt>.

A look at the browser will proove if these considerations are right:

<tt><a href="xxxATxxxDOTorg">Hannes Wallnöfer</a> <small><a href="http://localhost:8080/addressbook/1/edit">edit</a></small>
<a href="mailto:xxxATxxxDOTorg">Robert Gaggl</a> <small><a href="http://localhost:8080/addressbook/2/edit">edit</a></small>
<a href="mailto:xxxATxxxDOTorg">Tobi Schäfer</a> <small><a href="http://localhost:8080/addressbook/3/edit">edit</a></small></tt>

Great! It works. But yet the links lead into Helmatic nirvana. We have to enable the edit form first.


=== Handling User Input

To enter data into Helma from a browser we first create an adequate layout for an HTML form:

<tt>apps/addressbook/HopObject/edit.skin:</tt>
<pre>&lt;form action="edit" method="post"&gt;
First Name: &lt;input type="text" name="firstname"
    value="&lt;% this.firstname encoding="form" %&gt;" /&gt;&lt;br /&gt;
Last Name: &lt;input type="text" name="lastname"
    value="&lt;% this.lastname encoding="form" %&gt;" /&gt;&lt;br /&gt;
e-mail: &lt;input type="text" name="email"
    value="&lt;% this.email encoding="form" %&gt;" /&gt;
&lt;p /&gt;
&lt;input type="submit" name="submit" value=" Save " /&gt;
&lt;/form&gt;</pre>

Save this skin as <tt>edit.skin</tt> but this time in the <tt>HopObject</tt> directory (we will reveal the reason for this later).

The corresponding action <tt>edit.hac</tt> looks like this &#150; it needs to be saved in the <tt>Person</tt> directory:

<tt>apps/addressbook/Person/edit.hac:</tt>
<pre>res.data.title = "Edit Helma Address Book Entry";
res.data.body = this.renderSkinAsString("edit");
renderSkin("html");</pre>

Now the <tt>edit</tt> links from the main resource of our address book application should work and you should see the HTML form containing the chosen HopObject's data.

<% image name="addressbook" %>

The <tt>edit.hac</tt> / <tt>edit.skin</tt> combo works analogously to <tt>main.hac</tt> and <tt>link.skin</tt> in the previous sections. So nothing really new here except the <tt>encoding</tt> parameters in the skin markup.

They avoid HTML markup entered in the database from messing up the HTML layout. Each value returned from the macro handler is encoded like it was wrapped into a <a href="http://helma.server-side-javascript.org/reference/global.html#encodeForm"><tt>encodeForm()</tt></a> function.

Although the <tt>Save</tt> button already works, the data remains yet unchanged. The necessary code has to be added before the first line of <tt>edit.hac</tt>:

<pre>if (req.data.submit) {
&nbsp;&nbsp;this.firstname = req.data.firstname;
&nbsp;&nbsp;this.lastname = req.data.lastname;
&nbsp;&nbsp;this.email = req.data.email;
&nbsp;&nbsp;this.modifytime = new Date();
&nbsp;&nbsp;res.redirect(root.href());
}</pre>

Try out to change some values and click <tt>Save</tt> afterwards &#150; you should notice the changes immediately in the list view.


=== Creating A HopObject

We are only a few more steps away from successfully having built a simple application with the basic and most important Helma features.

One thing that's still missing is to create a new <tt>person</tt> HopObject. Let's do this now.

Because the create form almost exactly looks like the edit form in <tt>HopObject/edit.skin</tt> from the previous step, we will recycle it for this purpose.

Therefore some slight adjustments are necessary:

Change the first line of <tt>HopObject/edit.skin</tt> from

<pre>&lt;form action="edit" method="post"&gt;</pre>

to

<pre>&lt;form action="&lt;% response.action %&gt;" method="post"&gt;</pre>

Then add the line

<pre>res.data.action = "edit";</pre>

just before the last line containing <tt>renderSkin("html");</tt> in <tt>Person/edit.hac</tt> (this is just to keep this action file compatible with our changes).

Create the action file <tt>Root/create.hac</tt> as follows:

<tt>apps/addressbook/Root/create.hac:</tt>
<pre>if (req.data.submit) {
&nbsp;&nbsp;var p = new Person();
&nbsp;&nbsp;p.firstname = req.data.firstname;
&nbsp;&nbsp;p.lastname = req.data.lastname;
&nbsp;&nbsp;p.email = req.data.email;
&nbsp;&nbsp;p.createtime = new Date();
&nbsp;&nbsp;p.modifytime = new Date();
&nbsp;&nbsp;root.add(p);
&nbsp;&nbsp;res.redirect(root.href());
}

res.data.title = "Create Helma Address Book Entry";
res.data.body = this.renderSkinAsString("edit");
res.data.action = "create";
renderSkin("html");</pre>

Finally, add these three macro functions to a new file called <tt>macros.js</tt> (don't use <tt>Person/macros.js</tt>):

<tt>apps/addressbook/Root/macros.js:</tt>
<pre>function firstname_macro() {
&nbsp;&nbsp;return("");
}

function lastname_macro() {
&nbsp;&nbsp;return("");
}

function email_macro() {
&nbsp;&nbsp;return("");
}</pre>

Before we look at the result of these additions and modifications, let me explain some details.

The introduction of <tt>res.data.action</tt> and its corresponding macro <tt>&lt;% response.action %&gt;</tt> gives the skin file <tt>HopObject/edit.skin</tt> more flexibility since the action file that is invoking it determines the form action for it.

The reason why <tt>edit.skin</tt> is stored in the <tt>HopObject</tt> directory is to make it available for both, the <tt>Root</tt> and the <tt>Person</tt> prototypes. Everything that's belonging to the <tt>HopObject</tt> prototype also belongs to any other custom HopObject prototype. This way the <tt>Root</tt> and <tt>Person</tt> prototypes can share the same skin file.

Moreover, and this is a big difference to when we would have saved <tt>edit.skin</tt> as global skin file, we can use the scope variable <tt>this</tt>. It always refers to the actual HopObject, either the <tt>root</tt> object or a particular <tt>person</tt> object.

This is very convenient, since that way we can retrieve any HopObjects properties directly via <tt>this.firstname</tt>, <tt>this.lastname</tt> and <tt>this.email</tt>.

Because <tt>root</tt> does not have any of these properties, we have to avoid the output of error messages by adding corresponding macro functions.

So while the <tt>&lt;% this.firstname %&gt;</tt> macro for any <tt>person</tt> HopObject refers to its property stored in the database, <tt>&lt;% this.firstname %&gt;</tt> for the <tt>root</tt> HopObject refers to the macro function <tt>root.firstname_macro()</tt>, and respectively do the other two macros.

Now point your browser to the URL <a href="http://localhost:8080/addressbook/create">http://localhost:8080/<wbr>addressbook/<wbr>create</a> and you will get an empty form. Fill in some data and press <tt>Save</tt>. Et voilà! You immediately should see a new item at the end of the list.

Here is the file structure you should have ended up with:

&nbsp;- apps
&nbsp;&nbsp;&nbsp;- addressbook
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; db.properties
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- Global
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; html.skin
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- HopObject
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edit.hac
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edit.skin
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- Person
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; link.skin
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; macros.js
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type.properties
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- Root
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create.hac
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; macros.js
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; main.hac
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type.properties



     removed
     added