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:
- code libraries
- relational db mappings
- web context
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.