Helma 1.3 Design Document

Hannes Wallnöfer, April 2003
Version 0.0.1 [ideas in flow]

This document describes some design guidelines for version 1.3 of the Helma Object Publisher. Parts of this document refer to the current Helma design as of version 1.2, so this may not be a perfect introduction for complete Helma newbies.

overview

The rationale behind the planned Helma 1.3 release is to remain close enough to 1.2 so that existing applications can be ported to 1.3 with reasonable effort, but enhance the design of Helma 1.2 in some key areas. In other words, porting applications from 1.2 to 1.3 will not be free, but should be doable with some manageable amount of work.

On an architectural level, Helma 1.2 is based arond the concept of persistent objects that are mapped to relational database tables and scripted through prototypes. This concept of persistent object prototypes dictates a certain code layout and architecture to 1.2 applications. While it does give us the benefit of consistency, it puts some restrictions on application code that is not centered around persistent scripted objects. Among this is code that provides its own functionality, serves as interface to other software or as interface to the web.

The basic design idea behind this document is to create an infrastructure for application architectures that do not fit in well with Helma 1.2. The proposed solution is to create two alternative styles of Helma code layouts that will co-exist with the traditional persistent object prototype model. Application developers will then be able to mix and match these different styles of coding any way they see fit.

global script library

Code that is not related to any persistent entity object is basically limited to the global prototype in Helma 1.2. The fact that the global prototype is bundled to the application and can't be easily shared between applications further limits an app designer's options.

The proposed solution is to provide separate repositories for global library code. Code inside a global library would be equivalent to the global prototype in Helma 1.2. However, these repositories would not be limited to a flat directory, but could be structured with subdirectories. These subdirectories should be reflected in the scripting engine as namespaces. For example, with a JavaScript engine, code in a lib/http/client subdirectory would reside in lib.http.client.

These global script repositories, which may be per-application or shared between applications, would offer a convenient alternative to writing Helma extensions in Java. It should be possible to write extensions by placing a jar file into the lib/ext folder and providing some scripting glue code. Some hypothetical examples are provided below.

example 1: static functions for HTML rendering
   lib.html.renderDropdown(name, array, selected);
lib.html.renderInput(name, value, 14, 1);
example 2: HTTP client for POST requests
   var client = new lib.http.Post("http://myserver:2843/foo");
client.addParam("foo", "bat");
client.setContentType("foo/bar");
try {
var response = client.getResponse();
} catch (exception) {
// print error message
}

relational database mapping

Relational databases mapped to objects remain a central building block in Helma 1.3. The basic concept of database mapping and code layout shold remain the same. One thing that will be missing from Helma 1.3 is the root prototype, which will be replaced by web contexts (described below). Whether other features will be dropped in favor of any of the new possiblities Helma 1.3 will offer remains to be seen. If so, it should be reasonably easy to adapt old Helma 1.2 code to the new Helma 1.3 infrastructure.

The object-relational mapping properties files should be replaced with XML mapping files. It would be desirable if mappings could be generated in an automatical or semi-automatical manner. That could be done by browsing through defined data sources, selecting tables, and specifying the table to class mapping.

mapped data source tree

In Helma 1.2, all navigation through persistent objects is done through the application's object model. This limits object reachability in several ways. First, one has to know the object model in order to find one's way. Second, there is no unified way of navigating through objects. Third, only those collections and selections provided by the objectmodel are available. There is neither a default way for browsing the whole extent of one class, not specify conditions for the selection of object instances.

The proposed way to fix these problems in Helma 1.3 is to provide a separate data structure that reflects the mapped relational data sources. The data source tree operates with the same persistent HopObjects as the traditional Helma object model. Hence, objects from the data sources tree can be used within the app's object model and vice versa.

The exact capabilities of the data source tree remain to be specified. Below are a few suggestions/ideas:
   db.antville.text.get(243);
db.antville.text.get(243).copy();
db.antville.text.get(243).delete();
db.antville.text.get(243).clone();
db.antville.text.get({alias: "foo", foreign_key: 243});
db.antville.text.range(10, 20);
db.antville.text.add(obj);
db.antville.text.select("id < 243 and AV_SITE == 12");
Object caching won't be bound to applications in 1.3 but directly to the mapped data sources. This means that mapped relational data can be safely shared between applications. It may be possible to access properties of the data source cache through a scripting interface:
   db.antville.setCacheSize(100);
db.antville.clearCache();

web context

Applications will require one or more web contexts in order to talk HTTP in Helma 1.3.  Web contexts are directory structures that are published via HTTP. They may contain any files that are usually found on web sites, such as .html, .jpg, .zip files. Additionally, they contain files to connect to Helma. To invoke a Helma action, .hac files are used.

By default, directories within a web context will not be bound to any persistent object. However, it is possible to bind directories to specific objects using XML configuration files placed inside the directories. The exact syntax and capabilities  remain to be specified.

<!-- bind this directory to a static, non-persistent object (aka "mountpoint") -->
<binding type="static">
    <class>lib/image/ImageManager</class>
</binding>

<!-- bind this directory to a dynamically fetched persistent object (aka "collection")  -->
<binding type="database">
    <class>antville/Image</class>
    <accessname>av_alias</accessname>
    <filter>image_f_site = null</filter>
</binding>

<!-- bind this directory to a property of the parent directory's object  -->
<binding type="reference" name="owner" />

<!-- bind this directory to a collection of the parent directory's object  -->
<binding type="collection" name="stories" />

Having the possibliity to store Helma actions and related static web material in the same location bears some significant advantages. For one, Helma applications become easier to understand and more consistent. Features like running an onRequest function or action for all resources within a directory (no matter if static or dynamic) become pretty easy to implement.

actions, macros, skins

Actions, macros and skins remain the basic building blocks for object rendering in Helma 1.3.

actions
files with .hac extension or any function with a name ending with '_action'
macros
files with .mac extension or any function with a name ending with '_macro'
skins
files with .skin extension

One proposed change to actions and macros (provided they are inside a .hac or .mac file) is to let them have their own embedded skin. The embedded skin kan simply be rendered by calling renderSkin() without any argument. The action or macro ends and the skin begins when a line starting with three or more hash characters (#) is encountered:

res.write("1 2 ");
renderSkin();
### (action code ends here, embedded skin starts below)
3

The above action file would output "1 2 3".

The major update to skin functionality in Helma 1.3 is the introduction of an XML compliant macro tag syntax. Helma will parse skins for XML elements and attributes within a certain namespace (the default will most likely be "hop") and invoke macros for each found element or item. The precise format remains to be discussed on the mailing list and gong. Below are some sketches:

<span hop:macro="doobats">some text here</span>
<hop:macro name="bats">arf</hop:macro>

<hop:skin name="foo">arf arf arf</hop:skin>

<hop:set name="foo.bang" value="bar" />
<hop:get name="foo.bang" />

web server

Jetty has proved to be a great win and will remain the web server bundled with Helma. More Jetty configuration options will be available for Helma users. Ideally, it will be possible to configure Helma/Jetty just like standard Jetty, using Jetty's own XML configuration syntax.

applications

Helma 1.3 applications no longer are monolithic blocks of code, but bundles of resources of  various kinds:
Applications will be configured either within the Jetty config file or standalone XML files. The exact syntax remains to be specified. Below is a very simplistic hypothetical example.

<application name="antville">
    <lib>antville/slib</lib>
    <lib>slib</lib>
    <dbmap>antville/dbmap</dbmap>
    <web>antville/web</web>
</application>

credits

Many of the ideas in this document began life on an evening with Chris Langreiter back in January 2003. Much of the credit goes to him. Another important source of ideas came from Manfred Wuits, who at one of his visits introduced me to and helped me install and understand Typo3, from which some ideas are derived. Thanks to everybody else who made me think. You're welcome to continue doing so on the mailing list, on gong, or through direct interaction.