<?xml version='1.0'?>
<?xml-stylesheet href="docbook-css-0.4/driver.css" type="text/css"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" "http://www.oasis-open.org/docbook/xml/4.3b2/docbookx.dtd">
<article>

  <articleinfo>

    <title>Helma Documentation</title>

    <authorgroup>

      <author>

        <firstname>Michael</firstname>
        <surname>Platzer</surname>
        <affiliation>

          <address>

            <email>michael.platzer@knallgrau.at</email>

          </address>

        </affiliation>

      </author>

      <othercredit><firstname>Michael</firstname><surname>Jakl</surname></othercredit>
    </authorgroup>

    <legalnotice>

      <blockquote>

        <para>Copyright (c) 2005 by Michael Platzer. This material is 
        licensed under the Creative Commons Attribution-ShareAlike 2.0 
        Austria License. Terms and conditions for distribution can be 
        found at Creative Commons: 
        <ulink url="http://creativecommons.org/licenses/by-sa/2.0/at/deed.en">
        http://creativecommons.org/licenses/by-sa/2.0/at/deed.en</ulink>.</para>

      </blockquote>

    </legalnotice>

  </articleinfo>

  <sect1 id="Introduction">

    <title>Introduction</title>

    <sect2 id="IntroWhatIsHelma">

      <title>What is Helma?</title>

      <para><emphasis>Helma is a scriptable Java Web Application 
      Server.</emphasis></para>

      <para>Basically the web application market can be divided into 
      two segments. On the one hand, there are the server side 
      (interpreted) scripting languages (like Perl and PHP), which are 
      being supported and used by thousands of independent developers 
      ever since the early days of the dynamic web. On the other hand, 
      there are the web application servers (like JBoss, Tomcat, IBM 
      Websphere), generally developed, supported and sold by large 
      companies, requiring the in-depth knowledge of strictly-typed, 
      compiled languages as Java or C#. With Helma being a 
      &quot;scriptable Application Server&quot;, it tries to bring 
      together the benefits of both worlds.</para>

      <para>Helma is a mature piece of software, which has been in 
      production use for large, high-traffic news &amp; community sites 
      since years.</para>

      <para>Helma is mainly being developed and maintained by Hannes 
      Wallnoefer, a developer that is already well-known in the 
      open-source community for providing the Java implementation of 
      XML-RPC, which by now became part of the Apache foundation.</para>

    </sect2>

    <sect2 id="IntroWhyHelma">

      <title>Why Helma?</title>

      <para>

        So, what are the reasons to choose Helma in favour of another 
        application platform?
        <orderedlist>

          <listitem>

            <para>Applications are (generally) being scripted in 
            JavaScript (resp. EcmaScript), a standardized, well-known, 
            well-established, easy-to-learn scripting language.</para>

          </listitem>

          <listitem>

            <para>Any available Java-library can be made accessible for 
            the scripting environment.</para>

          </listitem>

          <listitem>

            <para>Helma forces an object-orientated application 
            design.</para>

          </listitem>

          <listitem>

            <para>Helma provides a transparent database-mapping, 
            freeing the developer from hassling around, and polluting 
            the code with SQL. As a side-effect, the relational 
            database becomes substitutable at any point by any other 
            JDBC-accessible database. Helma can even work without a 
            relational database, by falling back to a file-based 
            XML-database.</para>

          </listitem>

          <listitem>

            <para>Helma is Java. Scripted applications are compiled to 
            Java, and server-side Java has proven to be quite fast and 
            reliable, at least with the more recent Runtime 
            Environments.</para>

          </listitem>

          <listitem>

            <para>Helma provides a smart caching mechanism, which 
            minimizes the number of submitted select-queries to the 
            database, by keeping recently-used objects in memory. This 
            simple mechanism allows Helma to serve high-traffic sites, 
            without developers even start thinking about performance 
            tuning.</para>

          </listitem>

          <listitem>

            <para>Compared to other web application servers Helma is a 
            light-weight (~1,5MB download), has a rather small memory 
            footprint, and is fast on start-up.</para>

          </listitem>

          <listitem>

            <para>Helma is embeddable. It can be used to write a web 
            frontend to existing Java applications.</para>

          </listitem>

          <listitem>

            <para>If one Helma-server proves to be not sufficient for 
            the request load, more servers can be easily clustered 
            together. Again without requiring developers to rewrite 
            their applications.</para>

          </listitem>

          <listitem>

            <para>Scripted code is compiled to Java instantaneously, 
            i.e. as soon as the modified source file is saved to disk. 
            No need for developers to &quot;compile and run&quot; the 
            whole application, before they can see their modifications 
            in action.</para>

          </listitem>

          <listitem>

            <para>Helma provides a smart templating mechanism, which 
            forces a strict separation of content, logic and 
            presentation.</para>

          </listitem>

          <listitem>

            <para>Helma comes along with numerous extensions, which are 
            usually required for developing web applications. These 
            allow developers to easily</para>

            <orderedlist>

              <listitem>

                <para>send emails</para>

              </listitem>

              <listitem>

                <para>and write text-files</para>

              </listitem>

              <listitem>

                <para>and manipulate images</para>

              </listitem>

              <listitem>

                <para>a XML-RPC-client and -server</para>

              </listitem>

              <listitem>

                <para>access databases</para>

              </listitem>

              <listitem>

                <para>to a FTP-server</para>

              </listitem>

              <listitem>

                <para>generate Flash-Files</para>

              </listitem>

              <listitem>

                <para>much more.</para>

              </listitem>

            </orderedlist>

          </listitem>

          <listitem>

            <para>Helma provides transparent session management, i.e. 
            developers are freed from the task to assign requests to 
            sessions.</para>

          </listitem>

          <listitem>

            <para>Developers can easily generate clear, intuitive, 
            easy-to-remember, search-engine-friendly URLs.</para>

          </listitem>

          <listitem>

            <para>A scheduling mechanism is integrated into Helma, 
            which allows the automated execution of scripts at any 
            given time.</para>

          </listitem>

          <listitem>

            <para>Helma is open-source, and comes with a liberal BSD 
            style license. Its source-code can be studied, modified 
            (and also distributed) by anybody who wishes to. No license 
            costs are being charged, neither for private, nor for 
            commercial use. All the advantages of open-source software 
            apply to Helma. See the License itself: 
            <ulink url="http://helma.org/download/license/">
            http://helma.org/download/license/</ulink>.</para>

          </listitem>

        </orderedlist>

      </para>

    </sect2>

    <sect2 id="IntroWhatIsHelmaReally">

      <title>What is Helma really?</title>

      <para>The technology-affine reader might wonder by now, what 
      Helma really is. The Helma distribution consists of multiple Java 
      libraries, most of them provided by third-party, all having their 
      own specific task.</para>

      <para>These libraries are:</para>

      <variablelist>

        <varlistentry>

          <term>helma.jar</term>

          <listitem>

            <para>The core of Helma, which brings together all of the 
            following libraries and is in charge of Application 
            Management, Code loading, Request Path Resolution, Session 
            Management, DB mapping, Caching, Templating, Scheduling, 
            and more. Take a look at the JavaDocs (or the source code) 
            to get an in-depth view of Helma.</para>

          </listitem>

        </varlistentry>

        <varlistentry>

          <term>rhino.jar</term>

          <listitem>

            <para>The JavaScript to Java compiler, which is actively 
            developed by the Mozilla foundation. Read more about Rhino 
            at <ulink url="http://www.mozilla.org/rhino">
            http://www.mozilla.org/rhino</ulink>.</para>

          </listitem>

        </varlistentry>

        <varlistentry>

          <term>jetty.jar</term>

          <listitem>

            <para>The Jetty Server, which is also integrated with JBoss 
            for example, provides a powerful and stable web server, as 
            well as the AJP13-connection to integrate Helma with 
            (nearly) any other web server (via the mod_jk module 
            provided by the Apache foundation). See 
            <ulink url="http://jetty.mortbay.org">
            http://jetty.mortbay.org</ulink>.</para>

          </listitem>

        </varlistentry>

        <varlistentry>

          <term>jimi.jar</term>

          <listitem>

            <para>An image-manipulation library provided by Sun.</para>

          </listitem>

        </varlistentry>

        <varlistentry>

          <term>apache-dom.jar</term>

          <listitem>

            <para>Providing a XML- and HTML-DOM-Parser.</para>

          </listitem>

        </varlistentry>

        <varlistentry>

          <term>mail.jar</term>

          <listitem>

            <para>Sun&apos;s official JavaMail library.</para>

          </listitem>

        </varlistentry>

        <varlistentry>

          <term>crimson.jar</term>

          <listitem>

            <para>a XML-SAX-Parser.</para>

          </listitem>

        </varlistentry>

        <varlistentry>

          <term>netcomponents.jar</term>

          <listitem>

            <para>Providing (among other things) a Java 
            FTP-client.</para>

          </listitem>

        </varlistentry>

        <varlistentry>

          <term>servlet.jar</term>

          <listitem>

            <para>Providing the Java Servlet Api.</para>

          </listitem>

        </varlistentry>

        <varlistentry>

          <term>xmlrpc.jar</term>

          <listitem>

            <para>Implementation of a XML-RPC-client and server.</para>

          </listitem>

        </varlistentry>

        <varlistentry>

          <term>commons-fileupload.jar</term>

          <listitem>

            <para>File Upload Library. See 
            <ulink url="http://jakarta.apache.org/commons/fileupload/">
            http://jakarta.apache.org/commons/fileupload/</ulink>.</para>

          </listitem>

        </varlistentry>

        <varlistentry>

          <term>activation.jar</term>

        </varlistentry>

        <varlistentry>

          <term>commons-logging.jar</term>

        </varlistentry>

      </variablelist>

    </sect2>

  </sect1>

  <sect1 id="UsersGuide">

    <title>User&apos;s Guide</title>

    <sect2 id="GuideSettingUpHelma">

      <title>Setting up Helma</title>

      <sect3 id="GuideObtainingHelma">

        <title>Obtaining Helma</title>

        <para>Helma can be obtained in various forms from 
        <ulink url="http://www.helma.org">http://www.helma.org</ulink>. 
        Recommended for beginners are the latest stable Helma-packages 
        (either as .zip or .tgz), which also provide a number of demo 
        applications, to demonstrate the power of Helma. Simply 
        download that package, and unzip it anywhere on your 
        disk.</para>

        <para>As an alternative it is possible to download the 
        Helma-source-package, and compile Helma on your own. 
        Experienced users, who want to test the latest developments, 
        might want to download a nightly snapshot of Helma ( 
        <ulink url="http://adele.helma.org/download/helma/nightly/">
        http://adele.helma.org/download/helma/nightly/</ulink>), or 
        even fetch Helma (source) from the CVS repository ( 
        <ulink url="http://helma.org/development/cvs/howto/">
        http://helma.org/development/cvs/howto/</ulink>).</para>

      </sect3>

      <sect3 id="GuidePrerequisites">

        <title>Prerequisites</title>

        <para>The only prerequisite to get Helma up and running is a 
        Java Runtime Environment version 1.3 or higher. In order to use 
        Helma&apos;s new image manipulation code a version 1.4.1 or 
        higher is required. Sun&apos;s JRE 1.4.2 or 1.5 are recommended 
        for production use anyways.</para>

        <para>In order to generate JavaDocs or to compile Helma from 
        source, a JDK is required.</para>

      </sect3>

      <sect3 id="GuideGeneralFileStructureOfHelma">

        <title>General File Structure of Helma</title>

        <para>After download and unzipping the Helma-package, you will 
        find the following files and directories:</para>

        <variablelist>

          <varlistentry>

            <term><filename>apps</filename></term>

            <listitem>

              <para>The default directory for Helma to look for 
              application source code.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term><filename>lib</filename></term>

            <listitem>

              <para>In this directory all standard Helma-libraries are 
              stored, and loaded at startup.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term><filename>lib/ext</filename></term>

            <listitem>

              <para>A directory to place additional Java-libraries, 
              which should be available in the scripting environment. 
              This is also the put the JDBC-drivers.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term><filename>licenses</filename></term>

            <listitem>

              <para>A directory containing licenses of 
              third-party-libraries, that are packaged together with 
              Helma.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term><filename>scripts</filename></term>

            <listitem>

              <para>This directory contains useful scripts to run Helma 
              as a service.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term><filename>static</filename></term>

            <listitem>

              <para>The default directory for Helma to look for static 
              content.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term><filename>launcher.jar</filename></term>

            <listitem>

              <para>A helper Java-library, which is actually started by 
              the start-scripts, and which takes care of the loading of 
              all the other libraries.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term><filename>apps.properties</filename></term>

            <listitem>

              <para>This file defines the applications to be started 
              and also contains certain properties for these 
              applications.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term><filename>server.properties</filename></term>

            <listitem>

              <para>This file can contain server-wide settings.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term><filename>hop.bat</filename></term>

            <listitem>

              <para>A start-script for Windows-systems.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term><filename>hop.sh</filename></term>

            <listitem>

              <para>A start-script for Unix-based-systems.</para>

            </listitem>

          </varlistentry>

        </variablelist>

        <para>The following directories will be created after starting 
        Helma for the first time:</para>

        <variablelist>

          <varlistentry>

            <term><filename>db</filename></term>

            <listitem>

              <para>The default directory for Helma to keep its 
              file-based XML-database.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term><filename>log</filename></term>

            <listitem>

              <para>The default directory for Helma to store its 
              log-files.</para>

            </listitem>

          </varlistentry>

        </variablelist>

        <para>If you downloaded the Helma-source-package, you will find 
        the following additional directories:</para>

        <variablelist>

          <varlistentry>

            <term><filename>build</filename></term>

            <listitem>

              <para>Contains the Java build-tool ant, and 
              build-settings to compile Helma from sources, which is 
              done by calling &apos;<command>[HelmaHome]/build/build.sh 
              jar</command>&apos; on Linux, resp. &apos; <command>
              [HelmaHome]/build/build.bat jar</command>&apos; on 
              Windows. You will probably need to set the variable 
              JAVA_HOME within that script to the location of your 
              installed JDK for this to work</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term><filename>classes</filename></term>

            <listitem>

              <para>Contains the compiled Java-classes</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term><filename>docs</filename></term>

            <listitem>

              <para>Contains the generated JavaDocs.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term><filename>src</filename></term>

            <listitem>

              <para>Contains the source code for Helma, i.e. the 
              helma.jar.</para>

            </listitem>

          </varlistentry>

        </variablelist>

      </sect3>

      <sect3 id="GuideDevelopmentSetup">

        <title>Starting / Stopping Helma (Development Setup)</title>

        <para>Helma can be started by executing hop.bat on Windows, 
        respectively hop.sh on Unix-based systems. If java is not 
        present at the command line, or JAVA_HOME is not set as a 
        system variable, the start-script needs to be edited 
        accordingly, i.e. the JAVA_HOME needs to be set to the java 
        installation directory.</para>

        <para>Helma will log its output by default to several files in 
        the log-directory. For a development setup it is useful to have 
        all output being directed to the standard output, i.e. the 
        console. This is achieved by adding the line &apos;logDir = 
        console&apos; to the file <filename>
        [HelmaHome]/server.properties</filename>. Helma needs to be 
        restarted for this change to be effective.</para>

        <para>The default setting for Helma is to start a web server 
        port listening on port 8080, which means that you should be 
        able to access Helma after startup at 
        <ulink url="http://localhost:8080/">
        http://localhost:8080/</ulink> and/or at 
        <ulink url="http://127.0.0.1:8080/">
        http://127.0.0.1:8080/</ulink>.</para>

        <para>If you open the start-script in a text-editor, you will 
        see several startup-options that can be specified for 
        Helma:</para>

        <variablelist>

          <varlistentry>

            <term>HTTP_PORT</term>

            <listitem>

              <para>If this variable is specified Helma will start a 
              web server listening on that port.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>XMLRPC_PORT</term>

            <listitem>

              <para>If this variable is specified Helma will start a 
              XML-RPC-server listening on that port. Just needs to be 
              set, if you want to make certain functions callable via 
              XML-RPC.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>AJP13_PORT</term>

            <listitem>

              <para>If this variable is specified Helma will start a 
              AJP13-listener on that port, which is just required to 
              connect Helma to another Web Server via mod_jk. Port 8009 
              is the standard port for AJP13.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>RMI_PORT</term>

            <listitem>

              <para>If this variable is specified Helma will start a 
              RMI-listener on that port.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>JAVA_HOME</term>

            <listitem>

              <para>As mentioned before, this can/should point to the 
              base directory of the Java installation directory to be 
              used. The start script will look for the directory 
              &apos;bin&apos; and a file named &apos;java&apos; (resp. 
              &apos;java.exe&apos;) in that directory.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>JAVA_OPTIONS</term>

            <listitem>

              <para>Additional options that need to be passed to the 
              Java Runtime Environment.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>HOP_HOME</term>

            <listitem>

              <para>The home directory of Helma (where the libraries 
              need to reside in a subdirectory named &apos;lib&apos;) 
              can be set to something different than the directory of 
              the start script.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>OPTIONS</term>

            <listitem>

              <para>It is possible to set the location of the file 
              server.properties to something different than the 
              default, by specifying the &apos;-f&apos; option. Call 
              &apos;java jar launcher.jar --help&apos; for a complete 
              list of Helma startup options.</para>

            </listitem>

          </varlistentry>

        </variablelist>

        <para>It is possible to run multiple Helma instances on the 
        same machine. You only need to take care that these instances 
        all use separate ports, otherwise Helma will exit immediately 
        with a &quot;port is already in use&quot; error. The same error 
        will also appear if one of the ports is already in use by 
        another application (e.g. port 8080 is also used by Tomcat by 
        default). You will either have to change the conflicting port 
        in your start-script, or close the other application 
        first.</para>

        <para>Stopping Helma is simply done by exiting the Helma 
        process, which usually can be done by pressing Ctrl-C. 
        Restarting Helma is a matter of stopping and starting Helma 
        manually.</para>

        <para>Also make sure that the user, under which Helma is run, 
        has the necessary read-privileges for the lib- and 
        apps-directory, and write-privileges for the log- and the 
        db-directory.</para>

      </sect3>

      <sect3 id="GuideServerSetup">

        <title>Server Setup</title>

        <para>As it has been demonstrated in the previous section, it 
        is quite simple to get Helma up and running. To deploy Helma 
        for production use (on a server), a number of additional tasks 
        usually need to be performed.</para>

        <sect4 id="GuideHelmaAsServiceOnLinux">

          <title>Helma as Service on Linux</title>

          <para>It is advisable to install Helma as a service on Linux, 
          and have Helma being started and stopped automatically when 
          the server is started and stopped.</para>

          <para>This is achieved by copying the file <filename>
          [HelmaHome]/scripts/helma.conf</filename> to <filename>
          /etc/helma.conf</filename>, and <filename>
          [HelmaHome]/scripts/helma</filename> to <filename>
          /etc/init.d/helma</filename> (or to wherever the 
          Linux-Distribution keeps its service-scripts). Make sure that 
          the latter is actually executable (i.e. &apos;<command>chmod 
          u+x /etc/init.d/helma</command>&apos;), and that the settings 
          in <filename>/etc/helma.conf</filename> are correctly 
          configured (see that file for further documentation). Now it 
          is possible to start/stop Helma via the command &apos; 
          <command>helma start</command>&apos;, resp. &apos; <command>
          helma stop</command>&apos;. Additional actions are &apos; 
          <command>helma restart</command>&apos;, and &apos; <command>
          helma reload</command>&apos;.</para>

          <para>By copying the java-binary to something like 
          &apos;javahop&apos; (and adapt the JAVA_BIN-setting in 
          <filename>/etc/helma.conf</filename>), it is possible to 
          watch this process separately from other possible 
          Java-processes that are active on that server.</para>

          <para>It is also possible to run multiple Helma instances as 
          separate services. You just need to copy the script-files to 
          <filename>/etc/helma2.conf</filename> and <filename>
          /etc/init.d/helma2</filename> and adapt the 
          HELMA_CONFIG-setting in <filename>
          /etc/init.d/helma2</filename> accordingly.</para>

          <para>Depending on your Linux-Distribution you can 
          automatically instruct your system to start and stop Helma 
          when the server is being rebooted. On a Debian-System this is 
          for example done by executing the command &apos;update-rc.d 
          helma defaults&apos;.</para>

          <para>Important: If you decide to start any port (web, 
          xml-rpc,..) below the number 1024, which are considered as 
          &apos;privileged ports&apos; under Linux, then you have to 
          run Helma as the root user, otherwise these ports won&apos;t 
          be accessible.</para>

        </sect4>

        <sect4 id="GuideHelmaAsServiceOnWindows">

          <title>Helma as Service on Windows</title>

          <para>In order to install Helma as a Service on Windows 
          please refer to 
          <ulink url="http://adele.helma.org/download/helma/contrib/hannes/helmaservice/">
          http://adele.helma.org/download/helma/contrib/hannes/helmaservice/</ulink>.</para>

        </sect4>

        <sect4 id="GuideJavaStartupOptions">

          <title>Java Startup Options</title>

          <para>If you want to use Helma&apos;s image processing 
          capabilities on a Linux-Server without an X-Server (which is 
          generally the case), you need to specify 
          &apos;-Djava.awt.headless=true&apos; as a JAVA_OPTION.</para>

          <para>Depending on the Java Runtime Environment it is also 
          possible to specify a number of startup options in order to 
          tweak performance. Probably most important is the assignment 
          of the maximum (and minimum) memory size to be used by Helma, 
          which is done by &apos;-Xmx&apos;, resp. &apos;-Xms&apos;. 
          Helma&apos;s powerful object caching mechanism can become 
          quite memory-hungry, if cache size is set high. If you run 
          Helma on a multi-processor-system you also might want to 
          specify the garbage collector to something different than the 
          default. &apos;-XX:+UseParallelGC&apos; might prove very 
          effective in such a case.</para>

        </sect4>

        <sect4 id="GuideApacheSetup">

          <title>Apache Setup (via mod_jk)</title>

          <para>See the online-documentation for now: 
          <ulink url="http://helma.org/docs/howtos/mod_jk/ ">
          http://helma.org/docs/howtos/mod_jk/</ulink></para>

          <para>FIXME</para>

          <para>Include section on mod_ssl !</para>

        </sect4>

        <sect4 id="GuideTomcatSetup">

          <title>Tomcat Setup</title>

          <para>See the online-documentation for now: 
          <ulink url="http://helma.org/docs/howtos/lamtha/">
          http://helma.org/docs/howtos/lamtha/</ulink></para>

          <para>FIXME</para>

        </sect4>

        <sect4 id="GuideModGcjSetup">

          <title>modGcj Setup</title>

          <para>FIXME ??</para>

        </sect4>

      </sect3>

    </sect2>

    <sect2 id="GuideSettingUpAnApplication">

      <title>Setting up an Application</title>

      <sect3 id="GuideAppsProperties">

        <title>apps.properties</title>

        <para><emphasis>[HelmaHome]/apps.properties defines active 
        applications.</emphasis></para>

        <para>Assuming that Helma is run by the class helma.main.Server 
        (which is the case if Helma is started via one of the start 
        scripts, and is not embedded for example into Tomcat), the list 
        of active applications is defined by the file <filename>
        [HelmaHome]/apps.properties</filename>. Each line in that file, 
        that is neither a comment (i.e. starts with a &apos;#&apos;) 
        nor contains a period &apos;.&apos;, corresponds to an active 
        application. For each such defined application, it is possible 
        to specify further (optional) parameters.</para>

        <para>So, in order to start a new application, you just need to 
        add a new line to apps.properties, containing the application 
        name. This may also be done while Helma is running. Helma 
        detects changes to its property-files automatically, and 
        applies them immediately. In order to stop a running 
        application, the line containing the application name needs to 
        be commented out, or removed. The defined parameters can be 
        left untouched.</para>

        <programlisting># The next line tells Helma to start/run an  
# application named &apos;appname&apos; 
appname 
 
# The following lines list the available  
# optional parameters, together with their 
# default settings. 
appname.appdir = apps/appname 
appname.repository.0 = apps/appname 
appname.repository.0.implementation = \  
  helma.framework.repository.FileRepository 
appname.dbdir = db/appname 
appname.mountpoint = /appname 
appname.static 
appname.staticMountpoint 
appname.staticHome = index.html,index.htm 
appname.staticIndex = false 
appname.protectedStatic 
appname.uploadLimit = 4096 
appname.uploadSoftfail = false 
appname.cookieDomain 
appname.sessionCookieName = HopSession 
appname.protectedSessionCookie = true</programlisting>
        <variablelist>

          <varlistentry>

            <term>appdir</term>

            <listitem>

              <para>Defines the location of the source code repository 
              of that application. Can be either relative (to 
              [HelmaHome]), or absolute. The default location is 
              <filename>[HelmaHome]/apps/[appname]</filename>. We will 
              refer to this directory as [AppDir] in this 
              document.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>repository.X, repository.X.implementation</term>

            <listitem>

              <para>Alternatively to specifying a single code 
              repository, it is possible to define multiple 
              repositories. See the online documentation for details: 
              <ulink url="http://helma.org/development/rfc/77606/">
              http://helma.org/development/rfc/77606/</ulink> 
              (FIXME).</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>dbdir</term>

            <listitem>

              <para>Defines the location of the internal XML-based 
              database used by Helma for that application. Even if all 
              HopObjects are mapped to a relational database, this 
              directory is needed for certain files. Can be either 
              relative (to [HelmaHome]), or absolute. The default 
              location is <filename>
              [HelmaHome]/db/[appname]</filename>. We will refer to 
              this directory as [DbDir] in this document.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>mountpoint</term>

            <listitem>

              <para>This setting tells Helma which web requests should 
              be handled by that application. By default this is 
              /appname, i.e. all requests to 
              <ulink url="http://localhost:8080/appname">
              http://localhost:8080/appname</ulink> (assuming the 
              HTTP_PORT 8080 is enabled) are handled by that 
              application. It is also possible to set this value just 
              to &apos;/&apos;, so that the name of the application is 
              not contained in the URL anymore.</para>

              <para>Hint: For Apache/mod_jk/mod_rewrite/Helma-Setups it 
              is common practice to mount applications at 
              /helma/appname, so that just a single configuration line 
              is required to tell mod_jk what kind of requests should 
              be forwarded to Helma (i.e. those starting with 
              /helma).</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>static</term>

            <listitem>

              <para>Helma, resp. Jetty can also serve static content. 
              By default this is disabled, but can be enabled by 
              specifying &apos;static&apos;. This setting expects the 
              absolute or relative (with respect to <filename>
              [HelmaHome]</filename>) path to a directory, from which 
              static content should be served.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>staticMountpoint</term>

            <listitem>

              <para>If the serving of static content is enabled, the 
              URL for static content starts with the mountpoint of the 
              application with &apos;/static&apos; being appended to 
              that (e.g. 
              <ulink url="http://localhost:8080/appname/static">
              http://localhost:8080/appname/static</ulink>). By 
              specifying this setting, it is possible to change that 
              mountpoint.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>staticHome</term>

            <listitem>

              <para>Analog to Apache&apos;s DirectoryIndex-directive it 
              is possible to tell Jetty which file to serve, if a 
              static directory is requested. This can be a 
              comma-separated list of multiple filenames. The default 
              value is &apos;index.html,index.htm&apos;.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>staticIndex</term>

            <listitem>

              <para>By setting this to &apos;true&apos; 
              directory-listings are returned by Jetty. Similar to 
              Apache&apos;s mod_autoindex. The default behaviour is to 
              not return such listings.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>protectedStatic</term>

            <listitem>

              <para>Helma includes methods to serve static content from 
              the scripting environment (see 
              <link linkend="RefResForward">res.forward</link>). That 
              way it is possible to have some application code being 
              processed (for example to check for access-rights), 
              before actually serving that content. In order to enable 
              this feature, the protectedStatic-setting has to be set 
              to the location of a directory (either absolute, or 
              relative to [HelmaHome]) from which that content is 
              served.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>uploadLimit</term>

            <listitem>

              <para>Specifies the maximum size for uploaded files in 
              Kilobyte. The default is 1024, i.e. 1MB.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>uploadSoftfail</term>

            <listitem>

              <para>Determines how an upload, that exceeds the 
              uploadLimit should be handled. If this is explicitly set 
              to &quot;true&quot;, then Helma will not generate an 
              immediate error response, and just sets the property 
              helma_upload_error in the req.data object, which 
              indicates that an error has occured. Otherwise a 
              non-customizable HTTP error with status code 413 will be 
              thrown.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>cookieDomain</term>

            <listitem>

              <para>By default all cookies that are sent to the Client 
              with <link linkend="RefResSetCookie">res.setCookie</link> 
              are assigned to the same domain as the request went to. 
              If cookieDomain is set, and cookieDomain is a substring 
              of the requested domain, cookies are assigned to that 
              cookieDomain. This has the advantage, that the same 
              cookies can be shared across all sub-domains, by setting 
              the cookieDomain to something like 
              &apos;.domain.tld&apos;.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>sessionCookieName</term>

            <listitem>

              <para>By default Helma assigns requests to sessions 
              according to a session-cookie named 
              &apos;HopSession&apos;. By specifying the setting 
              sessionCookieName, it is possible to tell Helma to use 
              another name for that cookie.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>protectedSessionCookie</term>

            <listitem>

              <para>By default Helma binds the generated session cookie 
              to the first three octets of the client&apos;s 
              ip-address. This mechanism offers some protection against 
              possible cross-site-scripting attacks ( 
              <ulink url="http://en.wikipedia.org/wiki/Cross_site_scripting">
              http://en.wikipedia.org/wiki/Cross_site_scripting</ulink>), 
              but on the other hand can make Helma session&apos;s 
              mechanism unusable for some users (e.g. users with a 
              round-robin proxy setup). This mechanism can be disabled 
              by setting this property to &apos;false&apos;.</para>

            </listitem>

          </varlistentry>

        </variablelist>

        <para>As soon as the application is started, Helma will look 
        for the <filename>[AppDir]</filename>, and checks whether it 
        already exists and whether it contains at least one file. If 
        this is not the case, Helma will create four directories: 
        <filename>Global</filename>, <filename>Root</filename>, 
        <filename>HopObject</filename> and <filename>User</filename>. 
        Similar, the <filename>[DbDir]</filename> will be created, if 
        it does not exist yet.</para>

      </sect3>

      <sect3 id="GuideGeneralFileStructureOfAnApplication">

        <title>General File Structure of an Application</title>

        <para><emphasis>[AppDir] is the main code 
        repository.</emphasis></para>

        <para>The <filename>[AppDir]</filename> of a Helma application 
        can look something like this:</para>

        <para>

          <programlisting>[AppDir]/app.properties 
[AppDir]/db.properties 
[AppDir]/Global/ 
[AppDir]/Root/ 
[AppDir]/HopObject/ 
[AppDir]/User/ 
[AppDir]/PrototypeNameABC/ 
... 
[AppDir]/PrototypeNameXYZ/ 
[AppDir]/someExtraAppCode.zip 
[AppDir]/someExtraJavaLibrary.jar</programlisting>
        </para>

        <para>Each directory that is contained in <filename>
        [AppDir]</filename>, with the exception of <filename>
        Global</filename>, is interpreted as a scriptable prototype. 
        These prototypes are enriched with methods/functions by adding 
        (arbitrarily named) files with the file-extension 
        &apos;.js&apos;, with skins/templates by adding files with 
        file-extension &apos;.skin&apos;, and can be mapped to a 
        database table via a file named <filename>
        type.properties</filename>.</para>

        <para>A typical prototype directory might therefore look 
        something like this:</para>

        <programlisting>[AppDir]/PrototypeNameABC/type.properties 
[AppDir]/PrototypeNameABC/actions.js 
[AppDir]/PrototypeNameABC/macros.js 
[AppDir]/PrototypeNameABC/functions01.js 
[AppDir]/PrototypeNameABC/functions02.js 
[AppDir]/PrototypeNameABC/template01.skin 
[AppDir]/PrototypeNameABC/template02.skin 
[AppDir]/PrototypeNameABC/template03.skin</programlisting>
        <para>Additionally you might find files in existing 
        applications ending with &apos;.hsp&apos;, which are deprecated 
        template-files, and files ending with &apos;.hac&apos;, 
        representing web-accessible methods. Since web-accessible 
        methods are just like ordinary methods appended with 
        &apos;_action&apos;, it is now common practice to have them 
        being defined in &apos;.js&apos; just like ordinary 
        methods.</para>

        <para>If <filename>[AppDir]</filename> contains files ending 
        with &apos;.zip&apos;, they are assumed to be additional 
        application code. Helma treats these files, as if they were 
        unzipped in <filename>[AppDir]</filename>, whereas zipped code 
        and templates have a lower precedence than unzipped code.</para>

        <para>If <filename>[AppDir]</filename> contains files ending 
        with &apos;.jar&apos;, these are assumed to be extra Java 
        libraries that are being loaded in the runtime environment, and 
        are therefore available for scripting. Helma needs to be 
        restarted if such libraries are added or updated.</para>

        <para>All other files and subdirectories are being ignored by 
        Helma.</para>

      </sect3>

      <sect3 id="GuideAppProperties">

        <title>app.properties</title>

        <para><emphasis>[AppDir]/app.properties is the deployment 
        descriptor for an application.</emphasis></para>

        <para>This file can contain settings of various special 
        parameters used by Helma. The general form for all key/value 
        pairs follows this syntax:</para>

        <programlisting>keyNameABC = SomeStringValue 
# This line here just demonstrate how comments are being added</programlisting>
        <para>Examples for such parameters would be the setting of the 
        request timeout, the maximum number of Java threads to be 
        available at a time, or whether the logging of SQL should be 
        enabled. See the reference section (FIXME LINK) for a complete 
        list of internal parameters.</para>

        <para>Additionally this file can also contain any number of 
        custom key/value pairs, that can be accessed from within the 
        scripting environment. That value could then be accessed from 
        the application code via &apos;app.properties.keyNameABC&apos; 
        (with the key being case-insensitive). Purpose of this 
        mechanism is, to have all deployment-relevant settings being 
        stored in one common file, so that (theoretically) this would 
        be the only file to be modified in order to deploy an 
        application.</para>

        <para>The file &apos;[HelmaHome]/server.properties&apos; 
        follows the same syntax, and a similar purpose. It can be seen 
        as the deployment descriptor for a server. All defined 
        key/value pairs are also available in all applications, but can 
        be overridden by <filename>[AppDir]/app.properties</filename> 
        for each one. Generally it is a good advice to keep all 
        relevant custom-settings just in app.properties. Also see the 
        reference (FIXME LINK) section for server.properties.</para>

      </sect3>

      <sect3 id="GuideDbProperties">

        <title>db.properties</title>

        <para><emphasis>[AppDir]/db.properties defines connections to 
        relational databases.</emphasis></para>

        <para>This file can contain any number of relational data 
        sources. These sources can be used for Helma&apos;s DB mapping 
        mechanism, which maps prototypes to database tables, or to 
        submit SQL statements directly to a database.</para>

        <para>For each defined data source, the arguments 
        &apos;url&apos;, &apos;driver&apos;, &apos;user&apos; and 
        &apos;password&apos; need to be set. Any further arguments are 
        also passed along, when a new connection is established. The 
        name of the source can be arbitrarily chosen.</para>

        <programlisting>sourceName.url = jdbc:mysql://ServerName/DatabaseName 
sourceName.driver = NameOfJavaClassToBeUsedAsDriver 
sourceName.user = UserName 
sourceName.password = Password 
sourceName.XYZ = ABC</programlisting>
        <para>Examples of data sources for a MySQL, resp. an Oracle 
        database:</para>

        <programlisting>mysqlDB.url      = jdbc:mysql://localhost/db_twoday 
mysqlDB.driver   = org.gjt.mm.mysql.Driver 
mysqlDB.user     = helma 
mysqlDB.password = secret 
 
oracleDB.url      = jdbc:oracle://db.domain.com/oracle 
oracleDB.driver   = oracle.jdbc.driver.OracleDriver 
oracleDB.user     = helma 
oracleDB.password = secret</programlisting>
        <para>LINK: reference/db.properties</para>

      </sect3>

      <sect3 id="GuideClassProperties">

        <title>class.properties</title>

        <para><emphasis>[AppDir]/class.properties defines alternative 
        underlying classes for scripted prototypes.</emphasis></para>

        <para>FIXME</para>

        <para>See 
        <ulink url="http://helma.org/docs/properties/class.properties/">
        http://helma.org/docs/properties/class.properties/</ulink></para>

      </sect3>

    </sect2>

    <sect2 id="GuideScripting">

      <title>Scripting</title>

      <para>Helma provides an interface (namely the Java interface 
      helma.scripting.ScriptingEngine) through which theoretically any 
      Java scripting language can be used for writing application code 
      in Helma. Currently only JavaScript (Rhino) has been implemented 
      for Helma. Other possible candidates for such integration would 
      be Jacl, Jython, JRuby, JudoScript, Pnuts and Groovy.</para>

      <sect3 id="GuideJavaScript">

        <title>JavaScript</title>

        <para>So, in order to develop Helma applications a thorough 
        knowledge and understanding of the JavaScript language is 
        necessary. The better you learn the language from the beginning 
        on, the smoother your experience with Helma will be.</para>

        <para>The Rhino version currently shipped with Helma implements 
        JavaScript 1.5, which confirms to the 3rd edition of the 
        ECMA-262 ECMAScript standard, downloadable from 
        <ulink url="http://www.ecma-international.org/publications/standards/Ecma-262.htm">
        http://www.ecma-international.org/publications/standards/Ecma-262.htm</ulink>. 
        An extensive, but still readable guide can be found at 
        <ulink url="http://www.croczilla.com/~alex/reference/javascript_guide/index.html">
        http://www.croczilla.com/~alex/reference/javascript_guide/index.html</ulink>, 
        the according reference at 
        <ulink url="http://www.croczilla.com/~alex/reference/javascript_ref/index.html">
        http://www.croczilla.com/~alex/reference/javascript_ref/index.html</ulink>. 
        A much more comprehensive and visually enhanced overview on the 
        syntax is offered at http://javascript-reference.info/. But be 
        aware that often JavaScript references do not just document the 
        language itself, but also the so called host objects common to 
        client-side JavaScript (i.e. &apos;windows&apos;, 
        &apos;document&apos;, etc.). Helma is basically JavaScript 1.5 
        enhanced with a number of host objects typical for server-side 
        web development. A main part of this documentation is devoted 
        to the description of these additional objects.</para>

        <para>Some dummy JavaScript code:</para>

        <programlisting> 
// This a comment 
 
/* This is a 
   multi-line 
   comment */ 
 
var m; 
 
var i = 0; 
i = i + 3; 
 
var str = &quot;This is a String.&quot;; 
str += &quot; And another one appendend.&quot;; 
str += new String(&quot; And another.&quot;); 
str = str.substring(str.indexOf(&quot;.&quot;), str.length - 3); 
 
var bool = true; 
var isTrue = bool ? true : false; 
 
var arr = new Array(); 
arr[0] = &quot;value01&quot;; 
arr.push(&quot;value02&quot;); 
var str = arr.join(&quot; &amp; &quot;); 
arr.push([1,2,3,4]); 
var len = arr.length; 
 
var obj = new Object(); 
obj.key01 = &quot;value01&quot;; 
obj[&quot;key02&quot;] = str; 
obj.key03 = {name: &quot;product&quot;, price: 12.3}; 
obj.key04 = arr; 
 
var date = new Date(); 
var str = date.format(&quot;dd.MM.yyyy&quot;); 
 
var j = 0; 
for (var k=0; k&lt;10; k++) { 
   if (k%2 == 0) continue; 
   j += k; 
} 
 
try { 
   someUndefinedFunction(); 
} catch (err) { 
   return &quot;Error occurred!&quot;; 
}</programlisting>
      </sect3>

      <sect3 id="GuideRhino">

        <title>Rhino / Direct Scripting of Java</title>

        <para>Rhino extends the JavaScript language with a couple of 
        functions, as for example Array.prototype.toSource(), 
        uneval(Object) and seal(Object) (see 
        <ulink url="http://www.mozilla.org/rhino/rhino15R5.html">
        http://www.mozilla.org/rhino/rhino15R5.html</ulink> for 
        details).</para>

        <para>But more importantly, Rhino provides full access to any 
        kind of Java class that has been loaded into the Runtime 
        Environment. A feature that gives Helma developers (nearly) the 
        full power of Java at their fingertips, and let&apos;s them 
        take advantage of available Java libraries within their 
        application.</para>

        <para>Assuming that your Java classes are already loaded into 
        Helma (which is for example done by dropping your jared-library 
        into <filename>[HelmaHome]/lib/ext</filename> or to <filename>
        [AppDir]</filename>, and restarting Helma), you can call its 
        static methods, access constants, instantiate new objects, and 
        so forth.</para>

        <para>The following example demonstrates the use of the freely 
        available GeoIP Java API to look up country codes for 
        ip-addresses. See <ulink url="http://www.maxmind.com/app/java">
        http://www.maxmind.com/app/java</ulink> for more information on 
        this package.</para>

        <programlisting>var ls = new Packages.com.maxmind.geoip.LookupService(&quot;modGeoIP.dat&quot;, 
           Packages.com.maxmind.geoip.LookupService.GEOIP_MEMORY_CACHE); 
var code = ls.getCountry(&quot;81.223.46.226&quot;).getCode();</programlisting>
        <para>As you can see, Java classes are made accessible through 
        a top-level variable named Packages. The basic java classes 
        (starting with &apos;<command>java</command>.&apos;) do not 
        require this prefix, but are directly accessible. See the 
        following example:</para>

        <programlisting>var hash = new java.util.Hashtable(); 
hash.put(&quot;key01&quot;, &quot;a string&quot;); 
hash.put(&quot;key02&quot;, new Date());</programlisting>
        <para>Note that Rhino takes care of all the necessary type 
        conversion.</para>

        <para>Make sure that (at least at some point) you read through 
        the &apos;Scripting Java&apos; tutorial located on the Rhino 
        website: 
        <ulink url="http://www.mozilla.org/rhino/ScriptingJava.html">
        http://www.mozilla.org/rhino/ScriptingJava.html</ulink>, which 
        also explains how to instantiate Java arrays, implement 
        interfaces and call overloaded methods.</para>

      </sect3>

      <sect3 id="GuideHelmasHostObjects">

        <title>Helma&apos;s Host Objects</title>

        <para>As it has been mentioned before, Helma extends Rhino with 
        a number of host objects, providing extra functionality useful 
        for web scripting. These are:</para>

        <variablelist>

          <varlistentry>

            <term>req</term>

            <listitem>

              <para>Represents the HTTP request, that was sent by the 
              client.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>res</term>

            <listitem>

              <para>Represents the HTTP response, that is sent back to 
              the client.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>session</term>

            <listitem>

              <para>A session object, that is assigned to the current 
              request.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>app</term>

            <listitem>

              <para>Represents the application as a whole.</para>

            </listitem>

          </varlistentry>

        </variablelist>

        <para>Additionally the following two variables are available in 
        the scripting environment:</para>

        <variablelist>

          <varlistentry>

            <term>root</term>

            <listitem>

              <para>The root of the object model hierarchy.</para>

            </listitem>

          </varlistentry>

          <varlistentry>

            <term>path</term>

            <listitem>

              <para>An array of objects that are contained in the 
              request path.</para>

            </listitem>

          </varlistentry>

        </variablelist>

      </sect3>

    </sect2>

    <sect2 id="GuideHelmaBasics">

      <title>Helma Basics</title>

      <para>Before we will walk through Helma&apos;s functionality in 
      detail, we give a quick, very compressed overview over some of 
      its basic mechanism, and introduce a couple of Helma specific 
      terms.</para>

      <para>Helma applications generally follow an object orientated 
      approach. That is, developers define their prototypes (classes), 
      together with properties and methods. Instances of these 
      prototypes are called HopObjects, which are basically just 
      extended JavaScript objects. These prototypes are usually mapped 
      on a table of a relational database, each entry (row) of that 
      table representing a HopObject.</para>

      <para>Prototypes are being defined by adding new directories to 
      the <filename>[AppDir]</filename> (directory name = prototype 
      name), their functions by adding (arbitrarily named) js-files 
      containing function definitions into these directories. 
      Properties only need to be declared within <filename>
      [AppDir]/PrototypeNameABC/type.properties</filename> explicitly, 
      if they are going to be mapped on a relational database. Then a 
      property corresponds to specific column in that database 
      table.</para>

      <para>In addition to this basic mapping, it is also possible to 
      build object models and define relations between prototypes. A 
      prototype property can be a reference to another prototype, a 
      prototype can contain collections/lists of other prototypes, and 
      it is possible (and highly recommended) to define an object 
      hierarchy for these prototypes.</para>

      <para>The Root prototype is a special prototype that is always 
      present and immediately persistent as soon as the application is 
      started. It represents the root of the object model 
      hierarchy.</para>

      <para>The DB mapping and object modelling are defined in files 
      named &apos;<filename>type.properties</filename>&apos;, which 
      need to reside in the according prototype directories. Besides 
      these mappings, Helma developers (generally) do not have to mess 
      around with SQL. Any creating, retrieving, modifying and removing 
      of HopObjects happen within the scripting scope, with Helma 
      taking care of the building and execution of all the necessary 
      SQL statements.</para>

      <para>Besides having prototype specific methods, which all 
      require the existence of a HopObject (i.e. no static methods can 
      be defined), it is possible to define methods in a global 
      context, which can be directly called from anywhere in the 
      application code.</para>

      <para>There are four ways of how a function call can be 
      initiated:</para>

      <itemizedlist>

        <listitem>

          <para>a web request via HTTP</para>

        </listitem>

        <listitem>

          <para>a XmlRpc request</para>

        </listitem>

        <listitem>

          <para>an internal function call by the scheduler</para>

        </listitem>

        <listitem>

          <para>an external function call via 
          helma.main.launcher.Commandline</para>

        </listitem>

      </itemizedlist>

      <para>with the first type naturally being the most common one for 
      a web application.</para>

      <para>If a browser/client makes a web request to an application, 
      Helma tries to resolve the request path from left to right, 
      walking down the object hierarchy (starting with root) and trying 
      to find an according web accessible function. A prototype 
      function is web accessible if and only if its function name ends 
      with &apos;_action&apos;. This means that a request to 
      <ulink url="http://localhost:8080/appname/main">
      http://localhost:8080/appname/main</ulink> is handled by the 
      function main_action of the prototype Root. A request to 
      <ulink url="http://localhost:8080/appname/latestStories/437/show">
      http://localhost:8080/appname/latestStories/437/show</ulink> 
      (i.e. the request path handled by the application is 
      &apos;/latestStories/437/show&apos;) will have Helma fetch the 
      root object, fetch a collection named &apos;latestStories&apos; 
      attached to the root (if defined), fetch a HopObject (supposedly 
      of a prototype named &apos;Story&apos;) with ID 437 (if exists), 
      and call function show_action (if defined) on that HopObject. 
      That action will subsequently write to the response buffer, which 
      is then send back to the client as soon as the function returns. 
      Any parameter that is sent together with the request, no matter 
      whether that is a URL parameter (?key=value), or an element of a 
      submitted POST-form or a Cookie can be read via the Object 
      req.data.</para>

      <para>Aside from writing directly to the response, via the 
      function res.write, Helma provides a powerful and clean rendering 
      framework, consisting of so called skins and macros. Skins are 
      basically just parts of the response, usually being HTML code, 
      mixed with (tightly controlled) calls of certain functions, the 
      macros. These skins are either defined in a global context, or 
      for a specific prototype. By default skins are expected to be 
      file-based, stored with the file-extension &apos;.skin&apos; in 
      the according prototype directory, but can also be fetched from a 
      database or any other data source. A macro is simply a global or 
      prototype function with its name ending with &apos;_macro&apos;. 
      These macros are being replaced with some other string/text, 
      usually in dependence on certain conditions, when that skin is 
      being rendered.</para>

    </sect2>

    <sect2 id="GuideActions">

      <title>Actions</title>

      <para>Any defined prototype function with its name ending to 
      &apos;_action&apos; becomes automatically web accessible to any 
      HTTP client (which is generally a webbrowser), and is referred to 
      as &apos;action&apos; in this documentation. The following 
      example defines two such actions:</para>

      <programlisting>### Root/functions.js ### 
 
function main_action() { 
  res.write(&quot;The main action of the Root object.&quot;); 
} 
 
function hello_action() { 
  res.write(&quot;Hello&quot;); 
} </programlisting>
      <para>By requesting the URL 
      <ulink url="http://localhost:8080/appname/main">
      http://localhost:8080/appname/main</ulink> the first defined 
      action is being executed. A 
      <ulink url="http://localhost:8080/appname/hello">
      http://localhost:8080/appname/hello</ulink> would call the second 
      action. Actions that are named &apos;main&apos; are also taken as 
      the default action, if no particular action is being specified in 
      the request path. Trailing slashes in the request path are 
      ignored. Therefore, <ulink url="http://localhost:8080/appname">
      http://localhost:8080/appname</ulink>, 
      <ulink url="http://localhost:8080/appname/">
      http://localhost:8080/appname/</ulink>, 
      <ulink url="http://localhost:8080/appname/main">
      http://localhost:8080/appname/main</ulink> and 
      <ulink url="http://localhost:8080/appname/main/">
      http://localhost:8080/appname/main/</ulink> all result in the 
      same action call.</para>

      <para>Note, that function names, and therefore also action names 
      are case-sensitive, i.e. 
      <ulink url="http://localhost:8080/appname/main">
      http://localhost:8080/appname/main</ulink> and 
      <ulink url="http://localhost:8080/appname/Main">
      http://localhost:8080/appname/Main</ulink> are not resulting in 
      the same action call.</para>

      <para>Since the dot (&apos;.&apos;) is not allowed to be part of 
      a function name in JavaScript, an underscore needs to be used in 
      order to define an action containing a dot. The root action 
      &apos;style_css_action&apos; would therefore be accessible as 
      &apos; <filename>/appname/style.css</filename>&apos;, as well as 
      &apos; <filename>/appname/style_css</filename>&apos;. Being able 
      to specify dots in action names is important, since there are 
      still browser in use, that try to guess the content type of a 
      response from the extension of the requested 
      &apos;file&apos;.</para>

      <para>Helma provides a special function named onRequest(), which 
      can be defined for each prototype. This function will be 
      automatically called by Helma before any action is actually 
      processed for a prototype. Therefore that function is a good 
      place to implement functionality that the developer wants to make 
      sure to be always called, like checks for access 
      privileges.</para>

      <para>Example:</para>

      <programlisting> ### Root/functions.js ### 
 
function main_action() { 
  res.write(&quot; and Goodbye. &quot;); 
} 
 
function onRequest() { 
  res.write(&quot;Hello&quot;); 
}</programlisting>
      <para>Calling <ulink url="http://localhost:8080/appname/main">
      http://localhost:8080/appname/main</ulink> will result in the 
      string &apos;Hello and Goodbye&apos; being returned.</para>

    </sect2>

    <sect2 id="GuideRequests">

      <title>Requests</title>

      <para>Let&apos;s now have a look at a typical HTTP request being 
      submitted by a common browser to Helma:</para>

      <programlisting>GET /appname/main HTTP/1.1 
Host: localhost:8080 
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050317 Firefox/1.0.2 
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 
Accept-Language: en-us,en;q=0.5 
Accept-Encoding: gzip,deflate 
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 
keep-alive: 300 
Connection: keep-alive 
Cookie: HopSession=127.0.0.zsy455yhwnbe 
If-None-Match: &quot;O+nl/8BoDSesnGNp7ASjjw==&quot;</programlisting>
      <para>The first word of the first line contains the HTTP method, 
      which is generally GET. Also quite common is the HTTP method 
      POST, which can be used for submitting forms. Helma&apos;s 
      standard actions handle all incoming GET, POST and HEAD requests, 
      with the latter just returning the response header without the 
      actual body. In case that a developer wants to differentiate 
      between these methods, she can use req.isGet(), req.isPost() or 
      req.method to detect the actual method of the current 
      request.</para>

      <para>But the HTTP protocol is not limited to just these methods 
      (see 
      <ulink url="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html">
      http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html</ulink>). 
      An implementation of a REST API ( 
      <ulink url="http://en.wikipedia.org/wiki/Representational_State_Transfer">
      http://en.wikipedia.org/wiki/Representational_State_Transfer</ulink>) 
      for example, generally requires the handling of DELETE and PUT 
      requests. WebDAV ( 
      <ulink url="http://en.wikipedia.org/wiki/Webdav">
      http://en.wikipedia.org/wiki/Webdav</ulink>) even adds 7 more 
      additional methods to the protocol. Helma provides a flexible way 
      to handle all kind of possible methods separately. The developer 
      can define special actions with their function names ending with 
      &apos;_action_methodname&apos;, i.e. &apos;_action_delete&apos;, 
      &apos;_action_propfind&apos; and so forth. A DELETE request to 
      /appname/hello will initiate the execution of an action named 
      &apos;hello_action_delete&apos;.</para>

      <para>As can be seen in listed sample request, all kind of other 
      information is being passed along to the server besides the used 
      HTTP method. This data is exposed to Helma application code 
      through the request object &apos;req&apos;. See the 
      <link linkend="RefRequestObject">reference section</link> for a 
      detailed list of available methods and fields. Probably most 
      important is the Object req.data, that allows access to any form 
      data, url parameter or cookie value. A special case is the 
      uploading of a file, which is being discussed later on. 
      Additionally the class javax.servlet.http.HttpServletRequest is 
      directly exposed via req.servletRequest.</para>

    </sect2>

    <sect2 id="GuideResponses">

      <title>Responses</title>

      <para>A response is sent back to the client, as soon as the 
      action returns. This can either happen by making an explicit 
      return-call in the action, or by calling res.abort() or 
      res.redirect(), or happens if an error occurs, that is not being 
      caught.</para>

      <para>Now, let&apos;s investigate such a returned HTTP response 
      in detail:</para>

      <programlisting>HTTP/1.1 200 OK 
Date: Mon, 04 Apr 2005 22:02:20 GMT 
Server: Jetty/4.2.22 (Windows XP/5.1 x86 java/1.5.0_02) 
Last-Modified: Tue, 23 Oct 2001 09:54:46 GMT 
ETag: &quot;67987-2095-3bd53e66&quot; 
Accept-Ranges: bytes 
Content-Length: 8341 
Content-Type: text/html 
 
&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 3.2 Final//EN&quot;&gt; 
&lt;HTML&gt; 
... 
&lt;/HTML&gt;</programlisting>
      <para>This is the content of a typical, successful response, 
      consisting of a response header (containing some meta 
      information) and the body (containing the actual HTML to be 
      rendered). The outgoing response is exposed to the scripting 
      environment via the response object &apos;res&apos;. Again, see 
      the <link linkend="RefResponseObject">reference</link> for a 
      complete list of available methods and fields. The response 
      object offers a string-buffer, that the developer can write to. 
      This can either be done by directly calling the function 
      res.write() or by using renderSkin(), which renders a specific 
      skin to the response. Helma also provides a way to write back 
      binary content, which is done through the methods res.forward(), 
      resp. res.writeBinary(). Most of the common response header 
      fields can be set via the according fields of the response 
      object. Examples are res.contentType, res.charset, res.status, 
      and so forth.</para>

      <para>The response status code of a successfully processed 
      request is 200. A complete documentation of officially defined 
      status codes can be found here: 
      <ulink url="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">
      http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html</ulink>. 
      Two other common status codes are discussed in the 
      following:</para>

      <para>Status 303 represents a &apos;See other&apos;-response, 
      which is used to send the browser to some other URL, a task which 
      is very common for web applications. In Helma such a redirect is 
      triggered with the method res.redirect(url), which expects a URL 
      string as its single argument. As soon as Helma encounters such a 
      redirect statement, processing stops and the 303 response, 
      together with the new URL is immediately sent back.</para>

      <programlisting>### Root/functions.js ###  
 
function hello_action() { 
  res.redirect(&quot;http://localhost:8080/appname/ciao&quot;); 
} 
 
function ciao_action() { 
  res.write(&quot;Ciao!&quot;); 
}</programlisting>
      <para>In the above example a request to 
      <ulink url="http://localhost:8080/appname/hello">
      http://localhost:8080/appname/hello</ulink> would redirect the 
      browser immediately to 
      <ulink url="http://localhost:8080/appname/ciao">
      http://localhost:8080/appname/ciao</ulink>.</para>

      <para>Status 404 represents a &apos;Notfound&apos;-response. 
      Helma returns a 404 if there is no such defined action for a 
      particular request. It is possible to catch such 404 by 
      specifying a special notfound-action, which should be called as a 
      fallback in such a case. This mechanism can be used to display a 
      nicer formatted version of the 
      &apos;Notfound&apos;-response.</para>

      <programlisting>### Root/functions.js ### 
 
function notfound_action() { 
  res.write(&quot;Sorry, but nothing was found on this server.&quot;); 
}</programlisting>
      <para>A request to 
      <ulink url="A request to http://localhost:8080/appname/foo will actually have the above notfound_action being called. It should be noted, that it is even possible to define separate notfound_actions for each prototype, and it is also possible to change the name of that fallback action via the property &apos;notfound&apos; in app.properties.">
      http://localhost:8080/appname/foo</ulink> will actually have the 
      above notfound_action being called. It should be noted, that it 
      is even possible to define separate notfound_actions for each 
      prototype, and it is also possible to change the name of that 
      fallback action via the property &apos;notfound&apos; in 
      app.properties.</para>

      <para>In case that an error occurs during the processing of an 
      action, Helma will catch that error and return a status code 200 
      with the error message contained as its response body. Similar to 
      the notfound-action, it is also possible to define a special 
      fallback action, which should be called in such a case. This is 
      again useful for delivering a nicely rendered error message to 
      the client, or alternatively to set the status code to some other 
      value. That special function is named &apos;error_action&apos;, 
      and can again be implemented for each prototype, and can be 
      renamed by a property &apos;error&apos; in app.properties.</para>

      <para>A special error that might be thrown is the 
      Request-Timeout. By default Helma throws that error if the 
      processing of a request takes longer than 30 seconds. Since there 
      are applications, which might be very computing-intense, this 
      timeout-threshold can be changed through the setting 
      &apos;requestTimeout&apos; in app.properties.</para>

      <para>In case that the provided methods of the response object 
      are not sufficient, res.servletResponse exposes the class 
      javax.servlet.http.HttpServletResponse directly to the scripting 
      environment.</para>

    </sect2>

    <sect2 id="GuideGlobal">

      <title>Global</title>

      <para>Aside from the functions defined for specific prototypes, 
      it is also possible to define functions and macros (but not 
      actions) in a global context. This is done by adding js-files to 
      the directory <filename>[AppDir]/Global</filename>. The following 
      example demonstrates the calling of a root and a global function 
      from within a root action.</para>

      <programlisting>### Global/functions.js ### 
 
function foo() { 
  return &quot;a global function&quot;; 
} 
 
### Root/functions.js ### 
 
function foo() { 
  return &quot;a root function&quot;; 
} 
 
function main_action() { 
  res.write(root.foo() + &quot; and &quot; + foo()); 
}</programlisting>
      <para>A request to 
      <ulink url="http://localhost:8080/appname/main">
      http://localhost:8080/appname/main</ulink> will display the 
      string &apos;a root function and a global function&apos;. Note, 
      that opposed to prototypes it does not make sense to define 
      actions in a global context, since these can never be accessed 
      via a request path.</para>

    </sect2>

    <sect2 id="GuideFormData">

      <title>Handling Form Data and File Uploads</title>

      <para>This section on the handling of user input comes at a 
      rather early point in this User&apos;s Guide, since the following 
      sample applications require the user to interact with the 
      application through forms (as it is the nature with most web 
      applications). Knowing how to access such information is 
      therefore essential for understanding these latter 
      examples.</para>

      <para>As has already been mentioned earlier, all submitted form 
      (and cookie) data are available as properties of the object 
      &apos;req.data&apos;. Submitted information always comes in the 
      form of a key/value pair with the key-identifier being the name 
      of the input field. The submitted string of a text input field 
      with the name &apos;surname&apos;, will therefore be accessible 
      through req.data.surname, resp. req.data[&quot;surname&quot;]. 
      The method req.get(&quot;someKey&quot;) can also be used to 
      access that value, but is considered deprecated.</para>

      <para>Put the following code into an application directory named 
      &apos;formdemo&apos;, and start up that application by adding an 
      according line to <filename>
      [HelmaHome]/apps.properties</filename>.</para>

      <programlisting>### Root/forms.js ### 
 
function getForm_action() { 
   if (req.data.submit01 || req.data.submit02) { 
      res.write(&quot;F01: &quot; + req.data.field01 + &quot;&lt;br&gt;&quot;); 
      res.write(&quot;F02: &quot; + req.data.field02 + &quot;&lt;br&gt;&quot;); 
      res.write(&quot;F03: &quot; + req.data.field03 + &quot;&lt;br&gt;&quot;); 
      res.write(&quot;F04: &quot; + req.data.field04 + &quot;&lt;br&gt;&quot;); 
      res.write(&quot;F05: &quot; + req.data[&quot;field05_array&quot;].join(&quot;/&quot;) + &quot;&lt;br&gt;&quot;); 
      res.write(&quot;S01: &quot; + req.data.submit01 + &quot;&lt;br&gt;&quot;); 
      res.write(&quot;S02: &quot; + req.data.submit02 + &quot;&lt;br&gt;&quot;); 
   } 
   this.renderSkin(&quot;getForm&quot;); 
} 
 
function postForm_action() { 
   if (req.data.submit) { 
      res.write(&quot;F01: &quot; + encode(req.data.field01) + &quot;&lt;br&gt;&quot;); 
   } 
   this.renderSkin(&quot;postForm&quot;); 
} 
 
function uploadForm_action() { 
   if (req.data.submit) { 
      res.write(&quot;F01: &quot; + req.data.field01 + &quot;&lt;br&gt;&quot;); 
      var mimepart = req.data.field01; 
      res.write(&quot;Name: &quot; + mimepart.getName() + &quot;&lt;br /&gt;&quot;); 
      res.write(&quot;Size: &quot; + mimepart.getContentLength() + &quot;&lt;br /&gt;&quot;); 
      res.write(&quot;Type: &quot; + mimepart.getContentType() + &quot;&lt;br /&gt;&quot;); 
   } 
   this.renderSkin(&quot;uploadForm&quot;); 
} 
 
 
### Root/getForm.skin ### 
 
&lt;form method=&quot;GET&quot; action=&quot;/formdemo/getForm&quot;&gt; 
  &lt;input type=&quot;text&quot; name=&quot;field01&quot;&gt; 
  &lt;input type=&quot;hidden&quot; name=&quot;field02&quot; value=&quot;a hidden value&quot;&gt; 
  &lt;br /&gt; 
  &lt;select name=&quot;field03&quot;&gt; 
    &lt;option value=&quot;1&quot;&gt;1&lt;/option&gt; 
    &lt;option value=&quot;2&quot;&gt;2&lt;/option&gt; 
  &lt;/select&gt; 
  &lt;br /&gt; 
  &lt;input type=&quot;radio&quot; name=&quot;field04&quot; value=&quot;a&quot;&gt;a  
  &lt;input type=&quot;radio&quot; name=&quot;field04&quot; value=&quot;b&quot;&gt;b 
  &lt;br /&gt; 
  &lt;select name=&quot;field05&quot; size=&quot;3&quot; multiple&gt; 
    &lt;option value=&quot;x&quot;&gt;x&lt;/option&gt; 
    &lt;option value=&quot;y&quot;&gt;y&lt;/option&gt; 
    &lt;option value=&quot;z&quot;&gt;z&lt;/option&gt; 
  &lt;/select&gt; 
  &lt;br /&gt; 
  &lt;input type=&quot;submit&quot; name=&quot;submit01&quot; value=&quot;Go GET it! (01)&quot;&gt; 
  &lt;input type=&quot;submit&quot; name=&quot;submit02&quot; value=&quot;Go GET it! (02)&quot;&gt; 
&lt;/form&gt; 
 
### Root/postForm.skin ### 
 
&lt;form method=&quot;POST&quot; action=&quot;/formdemo/postForm&quot;&gt; 
  &lt;textarea name=&quot;field01&quot;&gt;&lt;/textarea&gt; 
  &lt;input type=&quot;submit&quot; name=&quot;submit&quot; value=&quot;Go POST it!&quot;&gt; 
&lt;/form&gt; 
 
### Root/uploadForm.skin ### 
 
&lt;form method=&quot;POST&quot; action=&quot;/formdemo/uploadForm&quot; enctype=&quot;multipart/form-data&quot;&gt; 
  &lt;input type=&quot;file&quot; name=&quot;field01&quot; accept=&quot;image/*&quot;&gt; 
  &lt;input type=&quot;submit&quot; name=&quot;submit&quot; value=&quot;Go UPLOAD it!&quot;&gt; 
&lt;/form&gt;
</programlisting>
      <para>The code defines three (Root-)actions, and accordingly 
      three (Root-)skins, which are being rendered from within these 
      actions. A request to 
      <ulink url="http://localhost:8080/formdemo/getForm">
      http://localhost:8080/formdemo/getForm</ulink> will render a 
      simple form with text input fields, radio buttons, dropdowns and 
      two submit-buttons. In <filename>Root/getForm.skin</filename> we 
      specified the used method to be &apos;GET&apos;, and defined that 
      same action as the action (i.e. the target) of that form. If the 
      user clicks on one of the submit buttons, the browser will send 
      the filled out form back to the getForm-action, which now renders 
      each of the submitted values at the top of the page. If you take 
      a look at the address bar of your browser, you will see that your 
      browser actually submitted the information to the server by 
      appending key/value-pairs at the end of the URL.</para>

      <para>Generally it is common practice to use the POST-method 
      instead of GET to submit data. In case that you have a textarea 
      (which allows multi-line text strings to be inserted), or want to 
      upload files, you actually must use the POST-form. The browser 
      will then send the submitted information not as part of the URL, 
      but appends it to the request body. Nevertheless the 
      req.data-object provides a transparent way to access that 
      information, no matter what method has been used. See 
      <ulink url="http://localhost:8080/formdemo/postForm">
      http://localhost:8080/formdemo/postForm</ulink> for an example. 
      After submitting the form, you will remain on the same action, 
      but the submitted text will now be displayed at the top of the 
      page. Try hitting the refresh-button in your browser, and you 
      will be prompted whether the browser should send the previously 
      submitted information again to the server. Therefore it is common 
      practice to perform a redirect, after a form has been processed, 
      so that a browser loses its memory and that a unintentional 
      re-submission of data is avoided. Note that the only reason why 
      we call the global method &apos;encode&apos; in our example.is 
      being called on the submitted string is to transform the 
      linebreaks of the textarea into actual HTML linebreaks, i.e. 
      br-tags.</para>

      <para>
      <ulink url="http://localhost:8080/formdemo/uploadForm">http://localhost:8080/formdemo/uploadForm</ulink> 
      demonstrates how to upload a file to the application. Firstly, 
      you need to use the POST-method, secondly you need to set the 
      enctype-attribute of the opening form-tag to 
      &quot;multipart/form-data&quot;, and thirdly you need to include 
      (at least) one form-field of type &quot;file&quot;. That way, the 
      browser knows how to correctly submit binary data to the server. 
      The according property in req.data now does not contain a string, 
      but a mimepart-object, which can be further processed (usually 
      written to disk). See the <link linkend="RefMimePart">
      documentation on the mimepart-object</link> for a full list of 
      available methods.</para>

      <para>As a final note on HTML forms two special cases should be 
      pointed out. Firstly, the form type &quot;checkbox&quot; does not 
      send any data at all, if it is not checked. Therefore it is not 
      possible to detect whether a checkbox has not been checked, or 
      whether it has not been present in the form at all. A way out of 
      this dilemma is to always include a second (hidden) field input, 
      that indicates the presence of that particular checkbox in the 
      form. Secondly, a submitted multi-select input form will contain 
      several key/value-pairs with identical keys. Accessing the 
      according property in req.data will only return of the submitted 
      values, but not all of them. Helma provides an additional 
      req.data-property appended with the key-name being appended with 
      &apos;_array&apos;, that returns an array of all selected 
      options. See the getForm-example above.</para>

    </sect2>

    <sect2 id="GuideCookies">

      <title>Cookies</title>

      <para>Besides prompting the user for data input through web 
      forms, it is possible to store and retrieve information on the 
      client side through the means of so called cookies, without the 
      interaction of the user. All common browsers support this 
      technology, whereas it is possible that cookies are not accepted 
      due to strict security settings. We distinguish between 
      session-cookies, which are removed from disk as soon as all 
      browser windows are being closed, and permanent cookies. The 
      latter can for example be used to track a client&apos;s action 
      over several sessions, or to store user credentials, so that the 
      user does not have to login each time she visits the web 
      application.</para>

      <programlisting>### Root/cookies.js ### 
 
function demoCookie_action() { 
   var cnt = req.data.counter; 
   if (cnt == null) cnt = 0; 
   res.write(&quot;Your access counter is at &quot; + cnt); 
   res.setCookie(&quot;counter&quot;, cnt + 1); 
}</programlisting>
      <para>The code example above will count the number of requests to 
      <ulink url="http://localhost:8080/appname/demoCookie">
      http://localhost:8080/appname/demoCookie</ulink>, and won&apos;t 
      lose track even if several days/weeks are between each of the 
      requests. Cookies are automatically created on the client-side 
      (if the client accepts cookies, which is generally the case), if 
      it did not exist before. Changing the value of a cookie is simply 
      done by re-setting that cookie. Removing a cookie can be done by 
      setting its value to an empty string.</para>

      <para>Since form data as well as cookie data are both available 
      as properties req.data, and can not be distinguished there, it is 
      sometimes necessary to use req.servletRequest.getCookies() in 
      order to make sure that a certain information has actually been 
      submitted as a cookie.</para>

    </sect2>

    <sect2 id="GuidePrototypes">

      <title>Prototypes</title>

      <para>FIXME</para>

      <programlisting>IGNORE THE FOLLOWING NOTES FOR NOW, AND JUST TAKE A LOOK AT THE SAMPLE CODE !!

Prototypes 
    persisting; fetching; removing 
    folders 
    fetching objects via ID 
    Actions: changes are just effective if no errors occur (res.abort, res.commit) 
    constructor() 
    inheritance - HopObject 
    HopObject 
        .add 
        .remove 
        .get 
        HopObject.getById()

Explain everything before we actually start showing some sample code; we use internal xml-based db for now;

Explain what we want to do in the sample app: Create a single prototype, create/persist, fetch, modify, and remove that object; List all existing persons with link to modification page, resp to delete-action; 

Create folder named &apos;Person&apos;;

Create minimalistic rendering framework, i.e. a simple global main-skin with res.data.body;

Root/main_action: loops through all existing persons, and builds string for res.data.body with id, name, Height and DateOfBirth

Root/createPerson_action: builds string with simple form to insert name, dateOfBirth and size with target=createPerson; if no name, then display message; otherwise create and persist new Person and redirect to main_action;

Insert link to deletePerson-action with personId as url-parameter in the Root/main_action-list

Root/deletePerson_action expects personId to be passed as url-string; writes out error, if no person is found; otherwise deletes that Person and redirects to root/main_action

Explain: how to fetch a persistent object, and how to delete it;

Insert link to Person/edit-action; contains a small form to change the name of that person; redirects to Root/main_action after successful update;

Explain: modification of existing objects;

Explain constructor(), HopObject-inheritance

calls list.skin;lists all existing person

Create root-action with small form to create new Person with certain &apos;name&apos;, and make that persistent; Root/createPerson_action();

create prototype &apos;person&apos;

hopobject-functions; HopObject-inheritance

properties id, name, dateofbirth, height

create Root/main_action that creates and lists all Persons

person-skin, person-macro, person-function

person-action; _parent ?? person-url?

href_macro ??</programlisting>
      <para>demoPrototypes.zip</para>

      <programlisting>
### HopObject/functions.js ### 
 
function info_action() { 
   res.write(&quot;HELLO &quot; + this._prototype.toUpperCase() + &quot; &quot; + this._id); 
} 
 
 
### Root/functions.js ### 
 
function main_action() { 
   // list all Persons 
   for (var i=0; i&lt;root.count(); i++) { 
      var p = root.get(i); 
      res.write(&quot;&lt;li&gt;&quot;); 
      res.write(p.name + &quot; &quot;); 
      if (p.dateofBirth) res.write(p.dateOfBirth.format(&quot;dd.MM.yyyy&quot;) + &quot; &quot;); 
      res.write(&quot;&lt;a href=&quot; + this.href(&quot;deletePerson&quot;) + 
         &quot;?personId=&quot; + p._id + &quot;&gt;delete this Person&lt;/a&gt; &quot;);
      res.write(&quot;&lt;a href=&quot; + p.href(&quot;edit&quot;) + &quot;&gt;edit this Person&lt;/a&gt;&quot;); 
   } 
   res.write(&quot;&lt;br /&gt;&quot;);
   res.write(&quot;&lt;a href=&quot; + this.href(&quot;createPerson&quot;) + &quot;&gt;create new Person&lt;/a&gt;&quot;);
} 
 

function createPerson_action() { 
   if (req.data.send) { 
      // instantiate a new object of type &apos;Person&apos; 
      var p = new Person(); 
      // set the property &apos;name&apos; of the new Person 
      p.name = req.data.name; 
      // set a integer value 
      p.bodySize = 180; 
      // set the date of birth 
      p.dateOfBirth = new Date(1978, 4, 24); 
      // store the new object persistently 
      root.add(p); 
      // now redirect the client to the list of all Persons 
      res.redirect(this.href(&quot;main&quot;)); 
   } 
   this.renderSkin(&quot;createPerson&quot;); 
} 
 
 
function deletePerson_action() { 
   // if personId is passed in URL, and such a Person exists... 
   if (req.data.personId &amp;&amp; root.get(req.data.personId)) { 
      // ... we delete that Person 
      var p = root.get(req.data.personId); 
      p.remove(); 
   } 
   // now redirect the client to the list of all Persons 
   res.redirect(this.href(&quot;main&quot;)); 
} 
 
 
### Root/createPerson.skin ### 
 
&lt;form action=&quot;&quot;&gt; 
  &lt;input type=&quot;text&quot; name=&quot;name&quot;&gt; 
  &lt;input type=&quot;submit&quot; name=&quot;send&quot; value=&quot;create new Person&quot;&gt; 
&lt;/form&gt; 
 
 
### Person/functions.js ### 
 
function edit_action() { 
   // if the form has been submitted... 
   if (req.data.send) { 
      // ... change the name of that person 
      this.name = req.data.name; 
      // ... and redirect to the root main action 
      res.redirect(root.href(&quot;main&quot;)); 
   } 
   this.renderSkin(&quot;edit&quot;); 
} 
 
 
function constructor() {
   // a constructor can be optionally defined for each
   // prototype in order to have programm code being
   // executed whenever a new Object is instantiated
   ;
} 
 
 
### Person/edit.skin ### 
 
&lt;form action=&quot;&quot;&gt; 
  &lt;input type=&quot;text&quot; name=&quot;name&quot;&gt; 
  &lt;input type=&quot;submit&quot; name=&quot;send&quot; value=&quot;change name&quot;&gt; 
&lt;/form&gt;
</programlisting>
    </sect2>

    <sect2 id="GuideDBMapping">

      <title>DB Mapping</title>

      <para>FIXME</para>

      <programlisting>IGNORE THE FOLLOWING NOTES FOR NOW, AND JUST TAKE A LOOK AT THE SAMPLE CODE !!

DB Mapping 
  JDBC driver 
  MySQL 
  needs ID 
  recall advantages: no necessity for sql 
  Caching Mechanism 
    HopObject.cache 
    clearCache 
    invalidate 
    http://helma.org/stories/77405/ 
  logSQL 
  basic mapping 
    _db 
    _table 
    _id 
    _idgen (for oracle) 
  Simple Property Mappings 
    prop.readonly 
    prop.private

Make sure that the SQL-Statements in mysql.sql are executed before the app is started.

demoSimpleMapping equals demoPrototypes with the difference, that objects are now stored in a relational database, and not in the internal XML database.

Turn on logSQL and watch what happens. Call app.clearCache and see what happens.</programlisting>
      <para>Theoretically any JDBC accessible database can be used: 
      MySQL, hsqldb (a pure Java database), Oracle, MS SQL Server, and 
      so forth. As an alternative to a relational database, 
      Helma&apos;s XML database can be used. This file-based database 
      is located at <filename>[HelmaHome]/db/appname</filename>, each 
      file in that directory corresponding to a HopObject. The 
      advantage is, that no explicit mapping is required and the 
      developer does not have to worry about database design up front. 
      On the other hand, this XML-database admittedly can not compete 
      with industry-proven relational databases as MySQL or Oracle 
      regarding reliability and scalability. Furthermore some of 
      Helma&apos;s object modelling features are just supported for 
      relational databases. For these reasons we will focus in this 
      documentation on an object model, that is mapped on such a 
      database.</para>

      <para>demoSimpleMapping.zip</para>

      <programlisting>
### mysql.sql ### 
 
CREATE DATABASE demoSimpleMapping; 
USE demoSimpleMapping; 
 
GRANT ALL ON demoSimpleMapping.* TO helma@localhost IDENTIFIED BY &apos;secret&apos;; 
 
CREATE TABLE person ( 
  person_id MEDIUMINT(10) NOT NULL, 
  person_name TINYTEXT, 
  person_height TINYINT unsigned, 
  person_dateofbirth DATETIME, 
  PRIMARY KEY (person_id) 
); 
 

### db.properties ### 
 
jad.url      = jdbc:mysql://localhost/demoSimpleMapping 
jad.driver   = org.gjt.mm.mysql.Driver 
jad.user     = helma 
jad.password = secret 
 
 
### Root/functions.js ### 
 
function main_action() { 
   // list all Persons 
   for (var i=0; i&lt;root.count(); i++) { 
      var p = root.get(i); 
      res.write(&quot;&lt;li&gt;&quot;); 
      res.write(p.name + &quot; &quot;); 
      if (p.dateofBirth) res.write(p.dateOfBirth.format(&quot;dd.MM.yyyy&quot;) + &quot; &quot;); 
      res.write(&quot;&lt;a href=&quot; + this.href(&quot;deletePerson&quot;) + 
         &quot;?personId=&quot; + p._id + &quot;&gt;delete this Person &lt;/a&gt; &quot;);
      res.write(&quot;&lt;a href=&quot; + p.href(&quot;edit&quot;) + &quot;&gt;edit this Person&lt;/a&gt;&quot;); 
   } 
   res.write(&quot;&lt;br /&gt;&lt;a href=&quot; + this.href(&quot;createPerson&quot;) + &quot;&gt;create new Person&lt;/a&gt;&quot;); 
} 
 
function createPerson_action() { 
   if (req.data.send) { 
      // instantiate a new object of type &apos;Person&apos; 
      var p = new Person(); 
      // set the property &apos;name&apos; of the new Person 
      p.name = req.data.name; 
      // set a integer value 
      p.height = 180; 
      // set the date of birth 
      p.dateOfBirth = new Date(1978, 4, 24); 
      // store the new object persistently 
      root.add(p); 
      // now redirect the client to the list of all Persons 
      res.redirect(this.href(&quot;main&quot;)); 
   } 
   this.renderSkin(&quot;createPerson&quot;); 
} 
 
function deletePerson_action() { 
   // if personId is passed in URL, and such a Person exists... 
   if (req.data.personId &amp;&amp; root.get(req.data.personId)) { 
      // ... we delete that Person 
      var p = root.get(req.data.personId); 
      p.remove(); 
   } 
   // now redirect the client to the list of all Persons 
   res.redirect(this.href(&quot;main&quot;)); 
} 
 
 
### Root/createPerson.skin ### 
 
&lt;form action=&quot;&quot;&gt; 
  &lt;input type=&quot;text&quot; name=&quot;name&quot;&gt; 
  &lt;input type=&quot;submit&quot; name=&quot;send&quot; value=&quot;create new Person&quot;&gt; 
&lt;/form&gt; 
 
 
### Person/type.properties ### 
 
_db = jad 
_table = person 
 
_id = person_id 
 
name = person_name 
height = person_height 
dateOfBirth = person_dateofbirth 
 
 
### Person/functions.js ### 
 
function edit_action() { 
   // if the form has been submitted... 
   if (req.data.send) { 
      // change the name of that person 
      this.name = req.data.name; 
      // redirect to the root main action 
      res.redirect(root.href(&quot;main&quot;)); 
   } 
   this.renderSkin(&quot;edit&quot;); 
} 
 
 
### Person/edit.skin ### 
 
&lt;form action=&quot;&quot;&gt; 
  &lt;input type=&quot;text&quot; name=&quot;name&quot;&gt; 
  &lt;input type=&quot;submit&quot; name=&quot;send&quot; value=&quot;change name&quot;&gt; 
&lt;/form&gt;
</programlisting>
    </sect2>

    <sect2 id="GuideObjectModelling">

      <title>Object Modelling</title>

      <para>FIXME</para>

      <programlisting>IGNORE THE FOLLOWING NOTES FOR NOW, AND JUST TAKE A LOOK AT THE SAMPLE CODE !!

Object Modelling 
  Object References 
    ref.local 
    ref.foreign 
    ref.local.X 
    ref.foreign.X 
    ref.logicalOperator 
  Collections 
    type.properties 
      _children 
      coll.local 
      coll.foreign 
      coll.loadmode 
      coll.cachemode 
      coll.order 
      coll.filter 
      coll.filter.additionalTables 
      coll.hints 
      coll.maxSize 
      coll.accessname 
    .count() 
    .add() 
    .removeChild() 
    .list() 
    .invalidate() 
    .contains() 
  Grouped Collections 
    coll.group 
    coll.group.order 
    coll.group.prototype 
  mountpoint 
  _parent</programlisting>
    </sect2>

    <sect2 id="GuideInheritance">

      <title>Inheritance</title>

      <para>FIXME</para>

      <programlisting>IGNORE THE FOLLOWING NOTES FOR NOW, AND JUST TAKE A LOOK AT THE SAMPLE CODE !!
</programlisting>
    </sect2>

    <sect2 id="GuideRequestPathResolution">

      <title>Request Path Resolution</title>

      <para>FIXME</para>

      <para>

        <programlisting>IGNORE THE FOLLOWING NOTES FOR NOW, AND JUST TAKE A LOOK AT THE SAMPLE CODE !!

Request Path resolution / URL space 
  notfound 
  error 
  baseuri 
  _parent for URL generation 
  .href() 
  hrefSkin, hrefFunction, hrefRootPrototype</programlisting>
      </para>

      <para>demoObjectModelling.zip: collections, parent, 
      object-reference</para>

      <programlisting>
### mysql.sql ### 
 
CREATE DATABASE demoObjectModelling; 
USE demoObjectModelling; 
 
GRANT ALL ON demoObjectModelling.* TO helma@localhost IDENTIFIED BY &apos;secret&apos;; 
 
CREATE TABLE tb_person ( 
  person_id MEDIUMINT(10) NOT NULL, 
  person_name TINYTEXT, 
  person_height TINYINT unsigned, 
  person_dateofbirth DATETIME, 
  person_org_id MEDIUMINT(10) unsigned 
  PRIMARY KEY (person_id) 
); 
 
CREATE TABLE tb_organisation ( 
   org_id MEDIUMINT(10) unsigned NOT NULL, 
   org_name TINYTEXT, 
   org_country TINYTEXT, 
   PRIMARY KEY (org_id) 
); 
 
CREATE INDEX idx_pers_org ON tb_person (person_org_id); 
 
INSERT INTO tb_organisation values (1, &quot;knallgrau&quot;, &quot;at&quot;); 
INSERT INTO tb_organisation values (2, &quot;helma&quot;, &quot;at&quot;); 
INSERT INTO tb_organisation values (3, &quot;amazon&quot;, &quot;us&quot;); 
INSERT INTO tb_organisation values (4, &quot;ebay&quot;, &quot;us&quot;); 
INSERT INTO tb_organisation values (5, &quot;yahoo&quot;, &quot;us&quot;); 
 
INSERT INTO tb_person values (1, &quot;michi&quot;, 180, &quot;1978-05-24&quot;, 1); 
INSERT INTO tb_person values (2, &quot;matthias&quot;, 179, &quot;1976-06-22&quot;, 1); 
INSERT INTO tb_person values (3, &quot;dieter&quot;, null, &quot;1978-06-13&quot;, 1); 
INSERT INTO tb_person values (4, &quot;hannes&quot;, null, null, 2); 
INSERT INTO tb_person values (5, &quot;weirdo&quot;, 185, null, null); 
 
 
### db.properties ### 
 
jad.url      = jdbc:mysql://localhost/demoObjectModelling 
jad.driver   = org.gjt.mm.mysql.Driver 
jad.user     = helma 
jad.password = secret 
 
 
### Root/type.properties ### 
 
_children = collection(Organisation) 
_children.accessname = org_name 
 
tallPeople = collection(Person) 
tallPeople.order = person_height desc 
tallPeople.filter = person_height IS NOT NULL 
# tallPeople.maxsize = 5 
 
allPeople = collection(Person) 
 
 
### Root/functions.js ### 
 
function main_action() { 
   // list all Organisation and their Persons 
   for (var i=0; i&lt;root.count(); i++) { 
      var o = root.get(i); 
      res.write(&quot;&lt;h3&gt;&quot; + o.name + &quot; (&quot; + o.country + &quot;)&lt;/h3&gt;&quot;); 
      for (var j=0; j&lt;o.count(); j++) { 
         var p = o.get(j); 
         res.write(&quot;&lt;li&gt;&quot;); 
         res.write(p.name + &quot; &quot; + (j+1) + &quot; of &quot; + p.organisation.count()); 
         res.write(&quot; &lt;a href=&quot; + p.href(&quot;info&quot;) + &quot;&gt;info&lt;/a&gt;&quot;); 
      } 
   } 
   res.write(&quot;&lt;br /&gt;&lt;a href=&quot; + root.href(&quot;tallPeople&quot;) + &quot;&gt;tall People&lt;/a&gt;&quot;); 
} 
 
 
function tallPeople_action() { 
   // list the tallest people 
   for (var i=0; i&lt;root.tallPeople.count(); i++) { 
      var p = root.tallPeople.get(i); 
      res.write(&quot;&lt;li&gt;&quot; + p.name + &quot; &quot; + p.height + &quot; &quot; + 
          p.href(&quot;info&quot;) + &quot; &quot; + root.get(&quot;knallgrau&quot;).contains(p)); 
   } 
} 
 
### Organisation/type.properties ### 
 
_db = jad 
_table = tb_organisation 
 
_id = org_id 
_parent = root 
 
_children = collection(Person) 
_children.local = org_id 
_children.foreign = person_org_id 
_children.accessname = person_name 
_children.order = person_name 
 
name = org_name 
country = org_country 
 
 
### Person/type.properties ### 
 
_db = jad 
_table = tb_person 
 
_id = person_id 
_parent = organisation, root.allPeople 
 
name = person_name 
height = person_height 
dateOfBirth = person_dateofbirth 
 
organisation = object(Organisation) 
organisation.local = person_org_id 
organisation.foreign = org_id
</programlisting>
      <para>demoGrouping.zip</para>

      <programlisting>
### mysql.sql ### 
 
CREATE DATABASE demoGrouping; 
USE demoGrouping; 
 
GRANT ALL ON demoGrouping.* TO helma@localhost IDENTIFIED BY &apos;secret&apos;; 
 
CREATE TABLE tb_person ( 
  person_id MEDIUMINT(10) NOT NULL, 
  person_name TINYTEXT, 
  person_height TINYINT unsigned, 
  person_dateofbirth DATETIME, 
  person_org_id MEDIUMINT(10) unsigned 
  PRIMARY KEY (person_id) 
); 
 
 
CREATE TABLE tb_organisation ( 
   org_id MEDIUMINT(10) unsigned NOT NULL, 
   org_name TINYTEXT, 
   org_country TINYTEXT, 
   PRIMARY KEY (org_id) 
); 
 
CREATE INDEX idx_pers_org ON tb_person (person_org_id); 
 
INSERT INTO tb_organisation values (1, &quot;knallgrau&quot;, &quot;at&quot;); 
INSERT INTO tb_organisation values (2, &quot;helma&quot;, &quot;at&quot;); 
INSERT INTO tb_organisation values (3, &quot;amazon&quot;, &quot;us&quot;); 
INSERT INTO tb_organisation values (4, &quot;ebay&quot;, &quot;us&quot;); 
INSERT INTO tb_organisation values (5, &quot;yahoo&quot;, &quot;us&quot;); 
 
INSERT INTO tb_person values (1, &quot;michi&quot;, 180, &quot;1978-05-24&quot;, 1); 
INSERT INTO tb_person values (2, &quot;matthias&quot;, 179, &quot;1976-06-22&quot;, 1); 
INSERT INTO tb_person values (3, &quot;dieter&quot;, null, &quot;1978-06-13&quot;, 1); 
INSERT INTO tb_person values (4, &quot;hannes&quot;, null, null, 2); 
INSERT INTO tb_person values (5, &quot;weirdo&quot;, 185, null, null); 
 
 
### db.properties ### 
 
jad.url      = jdbc:mysql://localhost/demoGrouping 
jad.driver   = org.gjt.mm.mysql.Driver 
jad.user     = helma 
jad.password = secret 
 
 
### Root/type.properties ### 
 
_children = collection(Organisation) 
_children.accessname = org_name 
 
countries = collection(Organisation) 
countries.group = org_country 
countries.group.prototype = Country 
countries.group.order = org_country desc 
 
 
### Root/functions.js ### 
 
function main_action() { 
   // list all Countries and their Organisations 
   for (var i=0; i&lt;root.countries.count(); i++) { 
      var c = root.countries.get(i); 
      res.write(&quot;&lt;h3&gt;&quot; + c.getName() + &quot; &lt;a href=&quot; + c.href(&quot;info&quot;) + &quot;&gt;info&lt;/a&gt;&lt;/h3&gt;&quot;); 
      res.write(&quot;&quot;); 
      for (var j=0; j&lt;c.count(); j++) { 
         var o = c.get(j); 
         res.write(&quot;&lt;li&gt;&quot;); 
         res.write(o.name + &quot; &quot; + o.count()); 
         res.write(&quot; &lt;a href=&quot; + o.href(&quot;info&quot;) + &quot;&gt;info&lt;/a&gt;&quot;); 
      } 
   } 
} 
 
 
### Organisation/type.properties ### 
 
_db = jad 
_table = tb_organisation 
 
_id = org_id 
_parent = root 
 
_children = collection(Person) 
_children.local = org_id 
_children.foreign = person_org_id 
_children.accessname = person_name 
_children.order = person_name 
 
name = org_name 
country = org_country 
 
 
### Person/type.properties ### 
 
_db = jad 
_table = tb_person 
 
_id = person_id 
_parent = organisation, root.allPeople 
 
name = person_name 
height = person_height 
dateOfBirth = person_dateofbirth 
 
organisation = object(Organisation) 
organisation.local = person_org_id 
organisation.foreign = org_id 
 
 
### Country/functions.js ### 
 
function getName() { 
   if (this.groupname == &quot;at&quot;)  
      return &quot;Austria&quot;; 
   else if (this.groupname == &quot;us&quot;)  
      return &quot;USA&quot;; 
}
</programlisting>
      <para>demoMNRelation.zip</para>

      <programlisting>### mysql.sql ### 
 
CREATE DATABASE demoMNRelation; 
USE demoMNRelation; 
 
GRANT ALL ON demoMNRelation.* TO helma@localhost IDENTIFIED BY &apos;secret&apos;; 
 
CREATE TABLE tb_person ( 
  person_id MEDIUMINT(10) NOT NULL, 
  person_name TINYTEXT, 
  PRIMARY KEY (person_id) 
); 
 
CREATE TABLE tb_relationship ( 
   rel_id MEDIUMINT(10) unsigned NOT NULL, 
   rel_person01_id MEDIUMINT(10) unsigned, 
   rel_person02_id MEDIUMINT(10) unsigned, 
   PRIMARY KEY (rel_id) 
); 
 
CREATE INDEX idx_rel_pers01 ON tb_relationship (rel_person01_id); 
CREATE INDEX idx_rel_pers02 ON tb_relationship (rel_person02_id); 
 
INSERT INTO tb_person values (1, &quot;michi&quot;); 
INSERT INTO tb_person values (2, &quot;matthias&quot;); 
INSERT INTO tb_person values (3, &quot;dieter&quot;); 
INSERT INTO tb_person values (4, &quot;hannes&quot;); 
 
INSERT INTO tb_relationship values (1, 1, 2); 
INSERT INTO tb_relationship values (2, 2, 1); 
INSERT INTO tb_relationship values (3, 1, 3); 
INSERT INTO tb_relationship values (4, 1, 4); 
INSERT INTO tb_relationship values (5, 4, 3); 
 
 
### db.properties ### 
 
jad.url      = jdbc:mysql://localhost/demoMNRelation 
jad.driver   = org.gjt.mm.mysql.Driver 
jad.user     = helma 
jad.password = secret 
 
 
### Root/type.properties ### 
 
allPeople = collection(Person) 
allPeople.accessname = person_name 
 
 
### Root/functions.js ### 
 
function main_action() { 
   // list all People and their friends 
   for (var i=0; i&lt;root.allPeople.count(); i++) { 
      var p = root.allPeople.get(i); 
      res.write(&quot;&lt;h3&gt;&quot; + p.name + &quot;&lt;/h3&gt;&quot;); 
      res.write(p.relations.count() + &quot; Friend(s)&quot;); 
      for (var j=0; j&lt;p.relations.count(); j++) { 
         var rel = p.relations.get(j); 
         var f = rel.person02; 
         res.write(&quot; &quot; + f.name); 
      } 
      res.write(&quot;&lt;br /&gt;Friend of &quot; + p.backrelations.count() + &quot; Person(s)&quot;); 
      for (var j=0; j&lt;p.backrelations.count(); j++) { 
         var rel = p.backrelations.get(j); 
         var f = rel.person01; 
         res.write(&quot; &quot; + f.name); 
      } 
   } 
 
   var michi = root.allPeople.get(&quot;michi&quot;); 
   var matthias = root.allPeople.get(&quot;matthias&quot;); 
   var dieter = root.allPeople.get(&quot;dieter&quot;); 
   res.write(&quot;&lt;hr&gt;&lt;b&gt;Is matthias a friend of michi?&lt;/b&gt; &quot;); 
   res.write(michi.isFriendOf(matthias) ? &quot;yes&quot; : &quot;no&quot;); 
   res.write(&quot;&lt;hr&gt;&lt;b&gt;Is matthias a friend of dieter?&lt;/b&gt; &quot;); 
   res.write(dieter.isFriendOf(matthias) ? &quot;yes&quot; : &quot;no&quot;); 
} 
 

 
### Person/type.properties ### 
 
_db = jad 
_table = tb_person 
 
_id = person_id 
_parent = root.allPeople 
 
name = person_name 
 
relations = collection(Relationship) 
relations.local = person_id 
relations.foreign = rel_person01_id 
relations.accessname = rel_person02_id 
 
backrelations = collection(Relationship) 
backrelations.local = person_id 
backrelations.foreign = rel_person02_id 
backrelations.accessname = rel_person01_id 
 
 
### Person/functions.js ### 
 
function isFriendOf(p) { 
   if (!p) return false; 
   // determine whether we have a relation to that person 
   /* 
   // the clumsy way 
   for (var i=0; i&lt;this.relations.count(); i++) { 
      if (this.relations.get(i).person02 == p) return true; 
   } 
   return false; 
   */ 
   // the smart way 
   if (this.relations.get(p._id+&quot;&quot;) != null)  
      return true; 
   else 
      return false; 
} 
 
 
### Relationship/type.properties ### 
 
_db = jad 
_table = tb_relationship 
 
_id = rel_id 
_parent = person01.relations 
 
person01 = object(Person) 
person01.local = rel_person01_id 
person01.foreign = person_id 
 
person02 = object(Person) 
person02.local = rel_person02_id 
person02.foreign = person_id
</programlisting>
      <para>demoMountpoint.zip</para>

      <programlisting>### mysql.sql ### 
 
CREATE DATABASE demoMountpoint; 
USE demoMountpoint; 
 
GRANT ALL ON demoMountpoint.* TO helma@localhost IDENTIFIED BY &apos;secret&apos;; 
 
CREATE TABLE tb_person ( 
  person_id MEDIUMINT(10) NOT NULL, 
  person_name TINYTEXT, 
  PRIMARY KEY (person_id) 
); 
 
INSERT INTO tb_person values (1, &quot;michi&quot;); 
INSERT INTO tb_person values (2, &quot;matthias&quot;); 
INSERT INTO tb_person values (3, &quot;dieter&quot;); 
INSERT INTO tb_person values (4, &quot;hannes&quot;); 
 
 
### db.properties ### 
 
jad.url      = jdbc:mysql://localhost/demoMountpoint 
jad.driver   = org.gjt.mm.mysql.Driver 
jad.user     = helma 
jad.password = secret 
 
 
### Root/type.properties ### 
 
allPeople = collection(Person) 
 
manage = mountpoint(SysMgr) 
 
 
### Root/functions.js ### 
 
function main_action() { 
   res.writeln(&quot;HELLO WORLD!&quot;); 
   res.writeln(&quot;&lt;a href=&quot; + root.manage.href(&quot;main&quot;) + &quot;&gt;SysMgr&lt;/a&gt;&quot;); 
   for (var i=0; i&lt;root.allPeople.count(); i++) { 
      var p = root.allPeople.get(i); 
      res.writeln(&quot;&lt;a href=&quot; + p.manage.href(&quot;main&quot;) + &quot;&gt;SysMgr of &quot; + p.name + &quot;&lt;/a&gt;&quot;); 
   } 
} 
 
 
### Person/type.properties ### 
 
_db = jad 
_table = tb_person 
 
_id = person_id 
_parent = root.allPeople 
 
name = person_name 
 
manage = mountpoint(SysMgr) 
 
  
### SysMgr/type.properties ### 
 
_children = collection(Person) 
 
 
### SysMgr/functions.js ### 
 
function main_action() { 
   res.writeln(&quot;TOP SECRET STUFF&quot;); 
   res.writeln(&quot;this: &quot; + this); 
   res.writeln(&quot;parent: &quot; + this._parent); 
   res.writeln(&quot;children: &quot; + this.count()); 
}
</programlisting>
    </sect2>

    <sect2 id="GuideRenderingFramework">

      <title>Rendering Framework</title>

      <para>FIXME</para>

      <programlisting>IGNORE THE FOLLOWING NOTES FOR NOW, AND JUST TAKE A LOOK AT THE SAMPLE CODE !!

Rendering Framework 
  Skins 
    file-based 
    res.skinpath 
    db-based 
    skinmanager 
    .skin 
    renderSkin 
    renderSkinAsString 
    createSkin() 
    res.data 
    allowMacro() 
    containsMacro() 
  Macros 
    prefix, suffix, encoding, default 
    macro handlers 
      global 
      default handlers 
        response 
        request 
        session 
        param 
      path/object hierarchy handler 
      res.handlers 
    all properties as macro (but not password) 
    res.push, res.pop 
    res.write or return ! 
  string manipulation 
    format 
    encodeXml 
    encode 
  Helma render framework tutorial 
    url: http://helma.org/docs/devguide/framework/ 
    Basics: The Helma Request Cycle 
    Separating Presentation from Logic 
    Actions, Skins and Macros 
    Enter the Prototypes 
    The render framework in action 
    Macro Handlers 
    Macro Attributes 
    Manipulating Macro Handlers with res.handlers 
    Advanced Skins: Skin Paths 
    Appendix: typical skinset setup with relational db mapping</programlisting>
      <para>
      <ulink url="http://helma.org/docs/devguide/framework/ ">http://helma.org/docs/devguide/framework/</ulink></para>

      <para>demoRenderingSkinHsp.zip</para>

      <programlisting> 
### mysql.sql ###  
  
CREATE DATABASE demoRenderingSkinHsp;  
USE demoRenderingSkinHsp;  
  
GRANT ALL ON demoRenderingSkinHsp.* TO helma@localhost IDENTIFIED BY &apos;secret&apos;;  
  
CREATE TABLE tb_person (  
  person_id MEDIUMINT(10) NOT NULL,  
  person_name TINYTEXT,  
  PRIMARY KEY (person_id)  
);  
  
INSERT INTO tb_person values (1, &quot;michi&quot;);  
INSERT INTO tb_person values (2, &quot;matthias&quot;);  
INSERT INTO tb_person values (3, &quot;dieter&quot;);  
INSERT INTO tb_person values (4, &quot;hannes&quot;);  
  
  
### db.properties ###  
  
jad.url      = jdbc:mysql://localhost/demoRenderingSkinHsp  
jad.driver   = org.gjt.mm.mysql.Driver  
jad.user     = helma  
jad.password = secret  
  
  
### Global/functions.js ###  
  
function aGlobalMacro_macro() {  
   res.write(&quot;a global macro&quot;);  
   return;  
}  
 
  
### Global/main.skin ###  
  
&lt;html&gt;  
  &lt;head&gt;  
  &lt;/head&gt;  
  &lt;body style=&quot;background-color:yellow&quot;&gt;  
    &lt;% response.body %&gt;  
  &lt;/body&gt;  
&lt;/html&gt;  
  
  
### HopObject/functions.js ###  
  
function href_macro(param) {  
   var action = param.action ? param.action : &quot;&quot;;  
   res.write(this.href(action));  
   return;  
}  
  
function skin_macro(param) {  
   this.renderSkin(param.name);  
   return;  
}  
  
  
### Root/type.properties ###  
  
persons = collection(Person)  
  
  
### Root/functions.js ###  
  
function main_action() {  
  
   // write directly to response, but we &apos;pop&apos; that string  
   // out of the response-buffer  
   res.push();  
   this.renderSkin(&quot;listAllPersons&quot;);  
   var str = res.pop();  
  
   // render hsp-template and string to response, but we &apos;pop&apos; that string  
   // out of the response-buffer  
   res.push();  
   this.createPersonTemplate();  
   this.renderSkin(&quot;createPerson&quot;);  
   str += res.pop();  
  
   // render hsp-template as string  
   str += this.createPersonTemplate_as_string();  
   // render skins as string  
   str += this.renderSkinAsString(&quot;createPerson&quot;);  
  
   res.data.body = str;  
   // actually render a skin directly to the response 
   renderSkin(&quot;main&quot;);  
   return;  
}  
  
  
function dummy_macro(param) {  
   var until = param.until ? parseInt(param.until, 10) : 5;  
   for (var i=0; i&lt;until; i++) {  
      res.write(i + &quot; &quot;);  
   }  
   return;  
}  
  
  
function listAllPersons_macro(param) {  
   for (var i=0; i&lt;root.persons.count(); i++) {  
      var p = root.persons.get(i);  
      p.renderSkin(&quot;listitem&quot;);  
   }  
   return;  
}  
  
  
### Root/createPerson.skin ###  
  
&lt;form action=&quot;&lt;% this.href action=&quot;createPerson&quot; %&gt;&quot; method=&quot;POST&quot;&gt;  
  &lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;  
  &lt;input type=&quot;submit&quot; name=&quot;send&quot;&gt;  
  &lt;% this.dummy until=&quot;10&quot; %&gt;  
  &lt;% this.skin name=&quot;hello&quot; %&gt;  
  &lt;% aGlobalMacro %&gt;  
&lt;/form&gt;  
  
  
### Root/createPersonTemplate.hsp ###  
  
&lt;form action=&quot;&lt;%= this.href(&quot;createPerson&quot;) %&gt;&quot; method=&quot;POST&quot;&gt;  
  &lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;  
  &lt;input type=&quot;submit&quot; name=&quot;send&quot;&gt;  
  &lt;% var until = 10;  
  for (var i=0; i&lt;until; i++) res.write(i + &quot; &quot;); %&gt;  
&lt;/form&gt;  
  
  
### Root/hello.skin ###  
  
Hello!  
  
  
### Root/listAllPersons.skin ###  
  
&lt;h2&gt;List of all Persons&lt;/h2&gt;  
&lt;% this.listAllPersons prefix=&quot;&lt;ul&gt;&quot; suffix=&quot;&lt;/ul&gt;&quot; default=&quot;no persons&quot; %&gt;  
  
  
### Person/type.properties ###  
  
_db = jad  
_table = tb_person  
  
_id = person_id  
_parent = root.persons  
  
name = person_name  
  
  
### Person/functions.js ###  
  
function getInfo() {  
   return &quot;just a person (&quot; + this.name + &quot;)&quot;;  
}  
  
  
### Person/listitem.skin ###  
  
&lt;li&gt;Name: &lt;% this.name %&gt;&lt;/li&gt; 
</programlisting>
      <para>demoRenderingMacroHandlers.zip</para>

      <programlisting>
### mysql.sql ###  
  
CREATE DATABASE demoRenderingMacroHandlers;  
USE demoRenderingMacroHandlers;  
  
GRANT ALL ON demoRenderingMacroHandlers.* TO helma@localhost IDENTIFIED BY &apos;secret&apos;;  
  
CREATE TABLE tb_person (  
  person_id MEDIUMINT(10) NOT NULL,  
  person_name TINYTEXT,  
  person_org_id MEDIUMINT(10) unsigned,  
  PRIMARY KEY (person_id)  
);  
  
  
CREATE TABLE tb_organisation (  
   org_id MEDIUMINT(10) unsigned NOT NULL,  
   org_name TINYTEXT,  
   org_country TINYTEXT,  
   PRIMARY KEY (org_id)  
);  
  
CREATE INDEX idx_pers_org ON tb_person (person_org_id);  
  
INSERT INTO tb_organisation values (1, &quot;knallgrau&quot;, &quot;at&quot;);  
INSERT INTO tb_organisation values (2, &quot;helma&quot;, &quot;at&quot;);  
INSERT INTO tb_organisation values (3, &quot;amazon&quot;, &quot;us&quot;);  
INSERT INTO tb_organisation values (4, &quot;ebay&quot;, &quot;us&quot;);  
INSERT INTO tb_organisation values (5, &quot;yahoo&quot;, &quot;us&quot;);  
  
INSERT INTO tb_person values (1, &quot;michi&quot;, 1);  
INSERT INTO tb_person values (2, &quot;matthias&quot;, 1);  
INSERT INTO tb_person values (3, &quot;dieter&quot;, 1);  
INSERT INTO tb_person values (4, &quot;hannes&quot;, 2);  
  
  
### db.properties ###  
  
jad.url      = jdbc:mysql://localhost/demoRenderingMacroHandlers  
jad.driver   = org.gjt.mm.mysql.Driver  
jad.user     = helma  
jad.password = secret  
  
  
### Global/main.skin ###  
  
&lt;html&gt;  
  &lt;head&gt;  
  &lt;/head&gt;  
  &lt;body style=&quot;background-color:yellow&quot;&gt;  
    &lt;% response.body %&gt;  
  
    &lt;hr /&gt;Macro Handlers Demonstration:&lt;br /&gt;  
      &lt;li&gt;response.key01: &lt;% response.key01 %&gt;&lt;/li&gt;  
      &lt;li&gt;request.key01: &lt;% request.key01 %&gt;&lt;/li&gt;  
      &lt;li&gt;session.key01: &lt;% session.key01 %&gt;&lt;/li&gt;  
      &lt;li&gt;param.key01: &lt;% param.key01 %&gt;&lt;/li&gt;  
  &lt;/body&gt;  
&lt;/html&gt;  
  
  
### HopObject/functions.js ###  
  
function href_macro(param) {  
   var action = param.action ? param.action : &quot;&quot;;  
   res.write(this.href(action));  
   return;  
}  
  
function skin_macro(param) {  
   this.renderSkin(param.name);  
   return;  
}  
  
  
### Root/type.properties ###  
  
_children = collection(Organisation)  
_children.accessname = org_name  
  
persons = collection(Person)  
  
manage = mountpoint(SysMgr)  
  
  
### Root/functions.js ###  
  
function main_action() {  
  
   res.handlers.SystemManager = root.manage;  
  
   res.push();  
   res.write(&quot;&lt;h2&gt;List of all Persons&lt;/h2&gt;&quot;);  
   for (var i=0; i&lt;root.persons.count(); i++) {  
      var p = root.persons.get(i);  
      p.renderSkin(&quot;info&quot;);  
   }  
  
   // create a skin from a string  
   var str = &quot;&lt;hr /&gt;Root Href: &lt;% this.href %&gt;&quot;;  
   var sk = createSkin(str);  
      // sk.allowMacro();  
      // sk.containsMacro();  
   this.renderSkin(sk);  
  
   res.data.body = res.pop();  
  
   // macro handler demo  
   res.data.key01 = &quot;value01&quot;;  
   // set req.data.key01 by appending &apos;?key01=value01&apos; to the URL  
   session.data.key01 = &quot;value01&quot;;  
   var obj = new Object();  
      obj.key01 = &quot;value01&quot;;  
      obj.key02 = &quot;value02&quot;;  
   renderSkin(&quot;main&quot;, obj);  
   return;  
}  
  
  
### Organisation/type.properties ###  
  
_db = jad  
_table = tb_organisation  
  
_id = org_id  
_parent = root  
  
name = org_name  
  
persons = collection(Person)  
persons.local = org_id  
persons.foreign = person_org_id  
  
allPersons = collection(Person)  
  
  
### Person/type.properties ###  
  
_db = jad  
_table = tb_person  
  
_id = person_id  
_parent = org.persons  
  
name = person_name  
  
org = object(organisation)  
org.local = person_org_id  
org.foreign = org_id  
  
  
### Person/functions.js ###  
  
function main_action() {  
  
   res.handlers.SystemManager = root.manage;  
  
   res.data.body = this.renderSkinAsString(&quot;info&quot;);  
   renderSkin(&quot;main&quot;);  
}  
  
  
### Person/info.skin ###  
  
&lt;li&gt;Name: &lt;a href=&quot;&lt;% this.href %&gt;&quot;&gt;&lt;% this.name %&gt;&lt;/a&gt; &lt;% Organisation.name %&gt; - &lt;% SystemManager.info %&gt;&lt;/li&gt; 
</programlisting>
      <para>demoRenderingSkinsets.zip</para>

      <programlisting>### mysql.sql ###  
  
CREATE DATABASE demoRenderingSkinsets;  
USE demoRenderingSkinsets;  
  
GRANT ALL ON demoRenderingSkinsets.* TO helma@localhost IDENTIFIED BY &apos;secret&apos;;  
  
CREATE TABLE tb_person (  
  person_id MEDIUMINT(10) NOT NULL,  
  person_name TINYTEXT,  
  person_org_id MEDIUMINT(10) unsigned,  
  PRIMARY KEY (person_id)  
);  
  
INSERT INTO tb_person values (1, &quot;michi&quot;, 1);  
INSERT INTO tb_person values (2, &quot;matthias&quot;, 1);  
INSERT INTO tb_person values (3, &quot;dieter&quot;, 1);  
INSERT INTO tb_person values (4, &quot;hannes&quot;, 2);  
  
  
CREATE TABLE tb_skinset (  
   skinset_id MEDIUMINT(10) unsigned NOT NULL,  
   PRIMARY KEY (skinset_id)  
);  
  
CREATE TABLE tb_skin (  
   skin_id MEDIUMINT(10) unsigned NOT NULL,  
   skin_proto TINYTEXT,  
   skin_name TINYTEXT,  
   skin_skin TEXT,  
   skin_set_id MEDIUMINT(10) unsigned NOT NULL,  
   PRIMARY KEY (skin_id)  
);  
  
INSERT INTO tb_skinset values (1);  
INSERT INTO tb_skinset values (2);  
INSERT INTO tb_skinset values (3);  
  
INSERT INTO tb_skin values (1, &quot;Global&quot;, &quot;main&quot;, &quot;&lt;body bgcolor=green&gt;&lt;% response.body %&gt;&lt;/body&gt;&quot;, 1);  
INSERT INTO tb_skin values (2, &quot;Global&quot;, &quot;main&quot;, &quot;&lt;body bgcolor=blue&gt;&lt;% response.body %&gt;&lt;/body&gt;&quot;, 2);  
  
  
### db.properties ###  
  
jad.url      = jdbc:mysql://localhost/demoRenderingSkinsets  
jad.driver   = org.gjt.mm.mysql.Driver  
jad.user     = helma  
jad.password = secret  
  
  
### Global/main.skin ###  
  
&lt;html&gt;  
  &lt;head&gt;  
  &lt;/head&gt;  
  &lt;body bgcolor=&quot;yellow&quot;&gt;  
    &lt;% response.body %&gt;  
  &lt;/body&gt;  
&lt;/html&gt;  
  
  
### HopObject/functions.js ###  
  
function href_macro(param) {  
   var action = param.action ? param.action : &quot;&quot;;  
   res.write(this.href(action));  
   return;  
}  
  
function skin_macro(param) {  
   this.renderSkin(param.name);  
   return;  
}  
  
  
### layouts/gray/Global/main.skin ###  
  
&lt;body bgcolor=&quot;gray&quot;&gt;&lt;% response.body %&gt;&lt;/body&gt;  
  
   
### layouts/orange/Global/main.skin ###  
  
&lt;body bgcolor=&quot;orange&quot;&gt;&lt;% response.body %&gt;&lt;/body&gt;  
  
  
### Root/type.properties ###  
  
persons = collection(Person)  
  
skinsets = collection(Skinset)  
  
  
### Root/functions.js ###  
  
function main_action() {  
  
   if (req.data.skinsetId) {  
      // we define the lookup-path for the skins 
      // the file-based skins residing in the code-directly are always used as the last fallback 
      res.skinpath = new Array(root.skinsets.getById(req.data.skinsetId).skinmanager, app.dir + &quot;/layouts/gray&quot;);  
   }  
   res.write(&quot;&lt;li&gt;&lt;a href=&quot; + this.href() + &quot;?skinsetId=1&gt;green&lt;/a&gt;&quot;);  
   res.write(&quot;&lt;li&gt;&lt;a href=&quot; + this.href() + &quot;?skinsetId=2&gt;blue&lt;/a&gt;&quot;);  
   res.write(&quot;&lt;li&gt;&lt;a href=&quot; + this.href() + &quot;?skinsetId=3&gt;default&lt;/a&gt;&quot;);  
  
   res.push();  
   res.write(&quot;&lt;h2&gt;List of all Persons&lt;/h2&gt;&quot;);  
   for (var i=0; i&lt;root.persons.count(); i++) {  
      var p = root.persons.get(i);  
      p.renderSkin(&quot;info&quot;);  
   }  
  
   res.data.body = res.pop();  
  
   renderSkin(&quot;main&quot;);  
   return;  
}  
  
  
### Skinset/type.properties ###  
  
_db = jad  
_table = tb_skinset  
  
_id = skinset_id  
_parent = root.skinsets  
  
skinmanager = collection(Skin)  
skinmanager.local = skinset_id  
skinmanager.foreign = skin_set_id  
skinmanager.group = skin_proto  
skinmanager.accessname = skin_name  
  
  
### Skin/type.properties ###  
  
_db = jad  
_table = tb_skin  
  
_id = skin_id  
_parent = skinset.skinmanager  
_name = skin_name  
  
skinset = object(Skinset)  
skinset.local = skin_id  
skinset.foreign = skin_set_id  
  
proto = skin_proto  
name = skin_name  
skin = skin_skin  
  
  
### Person/type.properties ###  
  
_db = jad  
_table = tb_person  
  
_id = person_id  
_parent = root.persons  
  
name = person_name  
  
  
### Person/info.skin ###  
  
&lt;li&gt;Name: &lt;a href=&quot;&lt;% this.href %&gt;&quot;&gt;&lt;% this.name %&gt;&lt;/a&gt;&lt;/li&gt; 
</programlisting>
    </sect2>

    <sect2 id="GuideSessionUserManagement">

      <title>Session &amp; User Management</title>

      <para>FIXME</para>

      <programlisting>IGNORE THE FOLLOWING NOTES FOR NOW, AND JUST TAKE A LOOK AT THE SAMPLE CODE !!

Session &amp; User Management 
  User 
    _name 
    password 
    app.registerUser(name, pwd) 
  session 
    HopSession 
      what if cookies are disabled 
    session.data 
    session.user 
    session.login(name, pwd) 
    session.login(User) 
    session.logout() 
    sessionTimeout 
  onLogout()</programlisting>
      <para>demoSessionUser.zip</para>

      <programlisting>
### mysql.sql ### 
 
CREATE DATABASE demoSessionUser; 
USE demoSessionUser; 
 
GRANT ALL ON demoSessionUser.* TO helma@localhost IDENTIFIED BY &apos;secret&apos;; 
 
CREATE TABLE tb_user ( 
  user_id MEDIUMINT(10) NOT NULL, 
  user_name TINYTEXT, 
  user_password TINYTEXT, 
  PRIMARY KEY (user_id) 
); 
 
INSERT INTO tb_user values (1, &quot;michi&quot;, &quot;ihcim&quot;); 
INSERT INTO tb_user values (2, &quot;matthias&quot;, &quot;saihttam&quot;); 
INSERT INTO tb_user values (3, &quot;dieter&quot;, &quot;reteid&quot;); 
INSERT INTO tb_user values (4, &quot;hannes&quot;, &quot;sennah&quot;); 
 
 
### db.properties ### 
 
jad.url      = jdbc:mysql://localhost/demoSessionUser 
jad.driver   = org.gjt.mm.mysql.Driver 
jad.user     = helma 
jad.password = secret 
 
 
### Root/type.properties ### 
 
users = collection(User) 
users.accessname = user_name 
 
 
### Root/functions.js ### 
 
function main_action() { 
   // first we try to auto-login the user 
   if (req.data.autoLoginName &amp;&amp; root.users.get(req.data.autoLoginName)) { 
      var usr = root.users.get(req.data.autoLoginName); 
      var hash = Packages.helma.util.MD5Encoder.encode(usr.name + usr.password); 
      if (hash == req.data.autoLoginHash) session.login(usr); 
   } 
 
   // display link to login, resp logout 
   if (session.user != null) { 
      res.writeln(&quot;Hi &quot; + session.user.name + &quot;!&lt;br /&gt;&quot;); 
      res.writeln(&quot;&lt;a href=&quot; + root.href(&quot;logout&quot;) + &quot;&gt;Logout&lt;/a&gt;&quot;); 
   } else { 
      res.writeln(&quot;&lt;a href=&quot; + root.href(&quot;login&quot;) + &quot;&gt;Login&lt;/a&gt;&quot;); 
      res.writeln(&quot;&lt;a href=&quot; + root.href(&quot;register&quot;) + &quot;&gt;Register&lt;/a&gt;&quot;); 
   } 
 
   // list all Users 
   res.write(&quot;&lt;hr&gt;&quot;); 
   res.write(&quot;&lt;h2&gt;Users&lt;/h2&gt;&quot;); 
   for (var i=0; i&lt;root.users.count(); i++) { 
      res.write(&quot;&lt;li&gt;&quot; + root.users.get(i).name); 
   } 
   return; 
} 
 
function login_action() { 
   if (req.data.login) { 
      var name = req.data.username; 
      var pass = req.data.password; 
      var usr = root.users.get(name); 
      if (usr &amp;&amp; usr.password == pass) { 
         session.login(usr); 
         if (req.data.remember) { 
            var hash = Packages.helma.util.MD5Encoder.encode(name + pass); 
            res.setCookie(&quot;autoLoginName&quot;, name, 30); 
            res.setCookie(&quot;autoLoginHash&quot;, hash, 30); 
         } 
         res.redirect(root.href()); 
      } else { 
         res.write(&quot;Login failed!&quot;); 
      } 
   } 
   this.renderSkin(&quot;login&quot;); 
} 
 
function logout_action() { 
   session.logout(); 
   res.setCookie(&quot;autoLoginName&quot;, &quot;&quot;); 
   res.setCookie(&quot;autoLoginHash&quot;, &quot;&quot;); 
   res.redirect(root.href()); 
} 
 
function register_action() { 
   if (req.data.login) { 
      var name = req.data.username; 
      var pass = req.data.password; 
      if (root.users.get(name) == null &amp;&amp; pass) { 
         var usr = new User(); 
         usr.name = name; 
         usr.password = pass; 
         root.users.add(usr); 
         session.login(usr); 
         res.redirect(root.href()); 
      } else { 
         res.write(&quot;Registration failed!&quot;); 
      } 
   } 
   this.renderSkin(&quot;register&quot;); 
} 
 
 
### Root/login.skin ### 
 
&lt;form action=&quot;&lt;% this.href action=&quot;login&quot; %&gt;&quot; method=&quot;POST&quot;&gt; 
  name: &lt;input type=&quot;text&quot; name=&quot;username&quot;&gt;&lt;br /&gt; 
  pass: &lt;input type=&quot;password&quot; name=&quot;password&quot;&gt;&lt;br /&gt; 
  remember me? &lt;input type=&quot;checkbox&quot; name=&quot;remember&quot; value=&quot;1&quot;&gt;&lt;br /&gt; 
  &lt;input type=&quot;submit&quot; name=&quot;login&quot; value=&quot;Login!&quot;&gt; 
&lt;/form&gt; 
 
 
### Root/register.skin ### 
 
&lt;form action=&quot;&lt;% this.href action=&quot;register&quot; %&gt;&quot; method=&quot;POST&quot;&gt; 
  name: &lt;input type=&quot;text&quot; name=&quot;username&quot;&gt;&lt;br /&gt; 
  pass: &lt;input type=&quot;password&quot; name=&quot;password&quot;&gt;&lt;br /&gt; 
  &lt;input type=&quot;submit&quot; name=&quot;login&quot; value=&quot;Register!&quot;&gt; 
&lt;/form&gt; 
 
 
### HopObject/functions.js ### 
 
function href_macro(param) { 
   var action = param.action ? param.action : &quot;&quot;; 
   res.write(this.href(action)); 
   return; 
} 
 
function skin_macro(param) { 
   this.renderSkin(param.name); 
   return; 
} 
 
 
### User/type.properties ### 
 
_db = jad 
_table = tb_user 
 
_id = user_id 
_parent = root.users 
 
name = user_name 
password = user_password
</programlisting>
    </sect2>

    <sect2 id="GuideExtensions">

      <title>Extensions</title>

      <sect3 id="GuideDirectDBConnection">

        <title>Direct DB Connection</title>

        <para>FIXME</para>

      </sect3>

      <sect3 id="GuideFileExtension">

        <title>File Extension</title>

        <para>FIXME</para>

      </sect3>

      <sect3 id="GuideFTPExtension">

        <title>FTP Extension</title>

        <para>FIXME</para>

        <para>See <ulink url="http://helma.org/docs/reference/ftp/">
        http://helma.org/docs/reference/ftp/</ulink> for now.</para>

      </sect3>

      <sect3 id="GuideImageExtension">

        <title>Image Extension</title>

        <para>FIXME</para>

        <para>See <ulink url="http://helma.org/docs/reference/image/">
        http://helma.org/docs/reference/image/</ulink> for now.</para>

      </sect3>

      <sect3 id="GuideMailExtension">

        <title>Mail Extension</title>

        <para>FIXME</para>

        <para>mail.charset, ISO-8859-15</para>

        <para>mail.host</para>

        <para>smtp</para>

        <para>See <ulink url="http://helma.org/docs/reference/mail/">
        http://helma.org/docs/reference/mail/</ulink> for now.</para>

      </sect3>

      <sect3 id="GuideXmlObjectExtension">

        <title>XmlObject Extension</title>

        <para>FIXME</para>

        <para>separator</para>

        <para>_mode</para>

        <para>See <ulink url="http://helma.org/docs/reference/xml/">
        http://helma.org/docs/reference/xml/</ulink> for now.</para>

      </sect3>

      <sect3 id="GuideXmlRpcClient">

        <title>XmlRpc Client</title>

        <para>FIXME</para>

        <para>See <ulink url="http://helma.org/docs/reference/xmlrpc/">
        http://helma.org/docs/reference/xmlrpc/</ulink> for now.</para>

      </sect3>

      <sect3 id="GuideXmlRpcServer">

        <title>XmlRpc Server</title>

        <para>FIXME</para>

        <para>See <ulink url="http://helma.org/docs/reference/xmlrpc/">
        http://helma.org/docs/reference/xmlrpc/</ulink> for now.</para>

      </sect3>

      <sect3 id="Guide3rdPartyExtensions">

        <title>3<superscript>rd</superscript> party Extensions</title>

        <para>e.g. Flash FIXME</para>

        <para>app.properties.extensions</para>

        <para>demoExtensions.zip</para>

        <programlisting> 
### mysql.sql ### 
 
CREATE DATABASE demoSessionUser; 
USE demoSessionUser; 
 
GRANT ALL ON demoSessionUser.* TO helma@localhost IDENTIFIED BY &apos;secret&apos;; 
 
CREATE TABLE tb_user ( 
  user_id MEDIUMINT(10) NOT NULL, 
  user_name TINYTEXT, 
  user_password TINYTEXT, 
  PRIMARY KEY (user_id) 
); 
 
INSERT INTO tb_user values (1, &quot;michi&quot;, &quot;ihcim&quot;); 
INSERT INTO tb_user values (2, &quot;matthias&quot;, &quot;saihttam&quot;); 
INSERT INTO tb_user values (3, &quot;dieter&quot;, &quot;reteid&quot;); 
INSERT INTO tb_user values (4, &quot;hannes&quot;, &quot;sennah&quot;); 


### app.properties ### 
 
XmlRpcAccess = api.demoXmlRpcServer 
XmlRpcHandlerName = *  

 
### db.properties ### 
 
jad.url      = jdbc:mysql://localhost/demoSessionUser 
jad.driver   = org.gjt.mm.mysql.Driver 
jad.user     = helma 
jad.password = secret 
 
 
### static/foto.jpg ### 
-&gt; put any king of image here 
 
 
### Root/type.properties ### 
 
api = mountpoint(Api) 
 
 
### Api/functions.js ### 
 
function demoXmlRpcServer() { 
   return &quot;HELLO WORLD!&quot;; 
} 
 
 
### Root/functions.js ### 
 
function main_action() { 
   return; 
} 
 
 
// make sure that you start Helma with enabled XML-RPC-Port at 8081 
// and that you have the following two lines in app.properties: 
//   XmlRpcAccess = api.demoXmlRpcServer 
//   XmlRpcHandlerName = * 
function demoXmlRpcClient_action() { 
   var client = new Remote(&quot;http://betty.userland.com/RPC2&quot;); 
   var i = 12; 
   var obj = client.examples.getStateName(i); 
   if (!obj.error) { 
      res.writeln(i + &quot;: &quot; + obj.result); 
   } else { 
      res.debug(obj.error); 
   } 
   res.writeln(&quot;-----------------------&quot;); 
   var client = new Remote(&quot;http://localhost:8081/&quot;); 
   var obj = client.api.demoXmlRpcServer(i); 
   if (!obj.error) { 
      res.writeln(obj.result); 
   } else { 
      res.debug(obj.error); 
   } 
} 
 
 
function demoDB_action() { 
   var dbc = getDBConnection(&quot;jad&quot;); 
   var sql = &quot;SELECT * from tb_user&quot;; 
   var rows = dbc.executeRetrieval(sql); 
   if (rows) { 
      while (rows.next()) { 
         var userId = rows.getColumnItem(&quot;user_id&quot;); 
         res.writeln(userId + &quot;: &quot; + rows.getColumnItem(&quot;user_name&quot;)); 
      } 
      rows.release(); 
      var sql = &quot;INSERT into tb_user values (&quot; + (userId+1) + &quot;, \&quot;joe&quot; + (userId+1) + &quot;\&quot;, \&quot;secret\&quot;)&quot;; 
      var i = dbc.executeCommand(sql); 
      if (i &gt; 0) { 
         res.writeln(&quot;Inserted new User &quot; + i); 
      } else { 
         res.debug(dbc.getLastError()); 
      } 
   } else { 
      res.debug(dbc.getLastError()); 
   } 
 
   dbc.release(); 
   return; 
} 
 
 
function demoFtp_action() { 
   // FIXME 
   return; 
} 
 
 
function demoXml_action() { 
   res.contentType = &quot;text/plain&quot;; 
   var obj = new HopObject(); 
      obj.name = &quot;michi&quot;; 
      obj.height = 180; 
   res.write(uneval(obj)); 
   res.write(&quot;\n-----------------\n&quot;); 
 
   var str = Xml.writeToString(obj) 
   res.write(str); 
   res.write(&quot;\n-----------------\n&quot;); 
 
   var obj2 = Xml.readFromString(str); 
   obj2.height++; 
   res.write(uneval(obj2)); 
   res.write(&quot;\n-----------------\n&quot;); 
} 
 
 
function demoImage_action() { 
   var staticDir = new File(app.dir, &quot;static&quot;); 
   var img = new Image(staticDir.getAbsolutePath() + &quot;/foto.jpg&quot;); 
   var w = img.getWidth(); 
   var h = img.getHeight(); 
   res.writeln(&quot;Image Original: &quot; + w + &quot;x&quot; + h); 
   img.resize(parseInt(w/2), parseInt(h/2)); 
   img.saveAs(staticDir + &quot;/foto_small.jpg&quot;); 
   img.dispose(); 
   res.write(&quot;&lt;img src=/static/justademo/foto_small.jpg&gt;&quot;); 
} 
 
 
function demoMail_action() { 
   var m = new Mail(); 
   m.addTo(&quot;michi@knallgrau.at&quot;, &quot;michi&quot;); 
   m.setFrom(&quot;bill@microsoft.com&quot;, &quot;Bill Gates&quot;); 
   m.setSubject(&quot;MS Office for free $$&quot;); 
   m.addText(&quot;Wow! This is so incredible.&quot;); 
   var f = new java.io.File(app.dir, &quot;test.txt&quot;); 
   if (f.exists()) m.addPart(f); 
   m.send(); 
   if (m.status == 0) { 
      res.write(&quot;Mail OK&quot;); 
   } else { 
      res.write(&quot;Error occurred! Make sure that you set smtp in your app.properties correctly!&quot; + m.status); 
   } 
   return; 
} 
 
 
function demoFile_action() { 
   var dir = new File(app.dir); 
   var files = dir.list(); 
   for (var i in files) { 
      var f = new File(dir, files[i]); 
      if (f.isFile()) { 
         res.writeln(&quot;File &quot; + f.getName()); 
      } else { 
         res.writeln(&quot;Dir &quot; + f.getName()); 
      } 
   } 
   res.write(&quot;&lt;hr&gt;&quot;); 
   var staticDir = new File(dir, &quot;static&quot;); 
   if (staticDir.exists() == false) staticDir.mkdir(); 
   var f = new File(staticDir + &quot;/test.txt&quot;); 
   res.writeln(&quot;File:&quot; + f.getAbsolutePath()); 
   if (f.exists()) { 
      res.writeln(&quot;Read and remove &quot; + f.getName()); 
      res.write(format(f.readAll())); 
      f.remove(); 
   } else { 
      f.open(); 
      var nr = 10; 
      for (var i=0; i&lt;nr; i++) { 
         f.writeln(&quot;Line &quot; + i); 
      } 
      f.close(); 
      res.writeln(&quot;Wrote &quot; + nr + &quot; lines to &quot; + f.getName()); 
   } 
   return; 
}
</programlisting>
      </sect3>

    </sect2>

    <sect2 id="GuideScheduler">

      <title>Scheduler</title>

      <para>schedulerInterval</para>

      <para>cronJobs</para>

      <para>FIXME</para>

      <para>
      <ulink url="http://helma.org/development/rfc/cronjobs/">http://helma.org/development/rfc/cronjobs/</ulink></para>

      <para>demoScheduler.zip</para>

      <programlisting>
### app.properties ### 
 
# schedulerInterval = 5 
 
cron.sthg.function = doSomething 
 
# cron.sthg.year = * 
# cron.sthg.month = 3,4,5 
# cron.sthg.day = 1-7,10-20 
# cron.sthg.weekday = monday,friday 
# cron.sthg.hour = 1-6 
# cron.sthg.minute = 0,5,10,15,20,25,30 
 
# cron.sthg.timeout = 60000 
 
 
### Global/functions.js ### 
 
function doSomething() { 
   app.log(&quot;doSomething now actually does something!!!&quot;); 
   return; 
}
</programlisting>
    </sect2>

    <sect2 id="GuideLogging">

      <title>Logging</title>

      <para>FIXME</para>

      <para>See app.log() [ 
      <ulink url="http://helma.org/stories/48012/">
      http://helma.org/stories/48012/</ulink>] for now.</para>

    </sect2>

    <sect2 id="GuideAspectJS">

      <title>AspectJS</title>

      <para>FIXME</para>

    </sect2>

    <sect2 id="GuidePerformanceTuning">

      <title>Performance Tuning</title>

      <para>FIXME</para>

    </sect2>

    <sect2 id="GuideClusteringHelma">

      <title>Clustering Helma</title>

      <para>FIXME</para>

    </sect2>

    <sect2 id="GuideCallingHelmaFromCommandline">

      <title>Calling Helma from Commandline</title>

      <para>Besides having Helma run as a server, it is also possible 
      to run a single script from command line. Helma will start up, 
      execute the script and terminate immediately. A possible usage 
      scenario would be for example to perform certain update scripts 
      from command line, if an installation of an application should be 
      upgraded. Another one is to generate the HelmaDocs via the 
      manage-application without actually having to start that 
      application.</para>

      <para>Usage:</para>

      <para><command>java -cp launcher.jar 
      helma.main.launcher.Commandline [options] [appname].[function] 
      [argument-list]</command></para>

      <para>Example:</para>

      <para><command>java -cp launcher.jar 
      helma.main.launcher.Commandline twoday.m.stories.1.dummy mi 
      mo</command></para>

      <para>In this example the function dummy() on the Story with the 
      Id 1 is called, with the arguments &apos;mi&apos; and 
      &apos;mo&apos; being passed to that function.</para>

    </sect2>

    <sect2 id="GuideHelmaLib">

      <title>helmaLib</title>

      <para>FIXME</para>

    </sect2>

    <sect2 id="GuideAntvilleLib">

      <title>antvilleLib</title>

      <para>FIXME</para>

    </sect2>

  </sect1>

  <sect1 id="Reference">

    <title>Reference</title>

    <sect2 id="RefHelmaCore">

      <title>Helma Core</title>

      <sect3 id="RefGlobalFunctions">

        <title>Global Functions</title>

        <para>This sections documents methods available in a global 
        context. For further details also see the JavaDocs for 
        helma.scripting.rhino.GlobalObject.</para>

        <variablelist>

          <varlistentry id="RefAuthenticate">

            <term>authenticate(String user, String pwd) returns 
            Boolean</term>

            <listitem>

              <para>Authenticates a user against a standard Unix 
              password file. Returns true if the provided credentials 
              match an according entry in the files <filename>
              [AppDir]/passwd</filename> or <filename>
              [HelmaHome]/passwd</filename>. The stored passwords in 
              these files must be either encrypted with the unix crypt 
              algorithm, or with the MD5 algorithm. The Apache web 
              server provides the utility tool &apos; <command>
              htpasswd</command>&apos; to generate such password 
              files.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefCreateSkin">

            <term>createSkin(String str) returns SkinObject</term>

            <listitem>

              <para>Creates a SkinObject from the passed String. The 
              returned object can be passed to the global functions 
              renderSkin, resp. renderSkinAsString. Also see the 
              <link linkend="RefSkinObject">reference on the 
              SkinObject</link>.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefEncode">

            <term>encode(String str) returns String</term>

            <listitem>

              <para>Performs the following string manipulations:</para>

              <itemizedlist>

                <listitem>

                  <para>All line breaks (i.e. carriage returns and line 
                  feeds) are replaced with &lt;br /&gt; tags.</para>

                </listitem>

                <listitem>

                  <para>All special characters (i.e. non ASCII) are 
                  being replaced with their equivalent HTML 
                  entity.</para>

                </listitem>

                <listitem>

                  <para>Existing markup tags are being encoded.</para>

                </listitem>

              </itemizedlist>

              <para>and returns the modified string.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefEncodeForm">

            <term>encodeForm(String str) returns String</term>

            <listitem>

              <para>Performs the following string manipulations:</para>

              <itemizedlist>

                <listitem>

                  <para>All special characters (i.e. non ASCII) are 
                  being replaced with their equivalent HTML 
                  entity.</para>

                </listitem>

                <listitem>

                  <para>Existing markup tags are being encoded.</para>

                </listitem>

              </itemizedlist>

              <para>and returns the modified string.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefEncodeXml">

            <term>encodeXml(String str) returns String</term>

            <listitem>

              <para>Performs the following string manipulations:</para>

              <itemizedlist>

                <listitem>

                  <para>All special characters (i.e. non ASCII) are 
                  being replaced with their equivalent HTML 
                  entity.</para>

                </listitem>

                <listitem>

                  <para>Existing tags, single and double quotes, as 
                  well as ampersands are being encoded.</para>

                </listitem>

                <listitem>

                  <para>Some invalid XML characters below 
                  &apos;0x20&apos; are removed.</para>

                </listitem>

              </itemizedlist>

              <para>and returns the modified string.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefFormat">

            <term>format(String str) returns String</term>

            <listitem>

              <para>Performs the following string manipulations:</para>

              <itemizedlist>

                <listitem>

                  <para>All line breaks (i.e. carriage returns and line 
                  feeds) are replaced with &lt;br /&gt; tags, with the 
                  exception of line breaks that follow certain block 
                  tags (e.g. table, div, h1, ..).</para>

                </listitem>

                <listitem>

                  <para>All special characters (i.e. non ASCII) are 
                  being replaced with their equivalent HTML 
                  entity.</para>

                </listitem>

              </itemizedlist>

              <para>and returns the modified string.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefFormatParagraphs">

            <term>formatParagraphs(String str) returns String</term>

            <listitem>

              <para>Performs the following string manipulations:</para>

              <itemizedlist>

                <listitem>

                  <para>All line breaks (i.e. carriage returns and line 
                  feeds) are replaced with &lt;br /&gt; tags, with the 
                  exception of line breaks that follow certain block 
                  tags (e.g. table, div, h1, ..).</para>

                </listitem>

                <listitem>

                  <para>Paragraphs are detected, and surrounded with 
                  &lt;p&gt; tags. Two following line breaks are 
                  considered to indicate a paragraph.</para>

                </listitem>

                <listitem>

                  <para>All special characters (i.e. non ASCII) are 
                  being replaced with their equivalent HTML 
                  entity.</para>

                </listitem>

              </itemizedlist>

              <para>and returns the modified string.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefGetDBConnection">

            <term>getDBConnection(String dbsource) returns 
            DatabaseObject</term>

            <listitem>

              <para>Connects to a relational database, and returns a 
              DatabaseObject representing that connection. The passed 
              string must match one of data sources being defined in 
              <filename>[AppDir]/db.properties</filename>, or an error 
              will be thrown. Also see the reference on the 
              DatabaseObject. (FIXME LINK)</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefGetHtmlDocument">

            <term>getHtmlDocument(Object src) returns 
            org.apache.html.dom.HTMLDocumentImpl</term>

            <listitem>

              <para>Tries to parse an object to a XML DOM tree using 
              Xerces&apos; HTML parser (nekohtml). The argument must be 
              either a URL, a piece of XML, an InputStream or a Reader. 
              Returns an instance of 
              org.apache.html.dom.HTMLDocumentImpl. See the JavaDocs 
              for that class for further details (LINK: -&gt; JavaDocs; 
              LINK: other recommended HTML parsers)</para>

              <para>CHECK with current Online Docs</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefGetProperty">

            <term>getProperty(String propname, String defvalue) returns 
            String</term>

            <listitem>

              <para>Returns any property defined in <filename>
              [AppDir]/app.properties</filename>, resp. 
              [HelmaHome]/server.properties that matches the passed 
              property name. This lookup is case-insensitive. Through 
              the optional second parameter it is possible to define a 
              default value that is being returned, in case that that 
              property has not been set.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefGetURL">

            <term>getURL(String url, Object option) returns 
            MimePart</term>

            <listitem>

              <para>Retrieves a file/document from the passed URL as a 
              MimePart Obejct, and therefore functions as a minimalist 
              version of a HttpClient. The optional second parameter 
              can either contain a (last-modified) date object, or an 
              eTag string, which will be passed along with the Http 
              request to the specified URL.</para>

              <para>Also see the <link linkend="RefMimePart">reference 
              on the MimePart Object</link>.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefGetXmlDocument">

            <term>getXmlDocument(Object src) returns Object</term>

            <listitem>

              <para>Tries to parse an object to a XML DOM tree using 
              the Crimson&apos; Parser. The argument must be either a 
              URL, a piece of XML, an InputStream or a Reader. Returns 
              an instance of org.apache.crimson.tree.XmlDocument. See 
              the JavaDocs for that class for further details (LINK: 
              -&gt; JavaDocs; LINK: other recommended XML 
              parsers)</para>

              <para>CHECK with current Online Docs</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefRenderSkin-1">

            <term>renderSkin(SkinObject skin, Object param)</term>

            <listitem>

              <para>Renders the passed SkinObject to the response 
              buffer.</para>

              <para>The properties of the optional parameter object are 
              accessible within the skin through the &apos;param&apos; 
              macro handler.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefRenderSkin-2">

            <term>renderSkin(String skinname, Object param)</term>

            <listitem>

              <para>Renders a global skin matching the passed name to 
              the response buffer.</para>

              <para>The properties of the optional parameter object are 
              accessible within the skin through the &apos;param&apos; 
              macro handler.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefRenderSkinAsString-1">

            <term>renderSkinAsString(SkinObject skin, Object param) 
            returns String</term>

            <listitem>

              <para>Returns the result of the rendered 
              SkinObject.</para>

              <para>The properties of the optional parameter object are 
              accessible within the skin through the &apos;param&apos; 
              macro handler.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefRenderSkinAsString-2">

            <term>renderSkinAsString(String skinname, Object param) 
            returns String</term>

            <listitem>

              <para>Returns the result of a rendered global skin 
              matching the passed name to the response buffer.</para>

              <para>The properties of the optional parameter object are 
              accessible within the skin through the &apos;param&apos; 
              macro handler.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSeal">

            <term>seal(Object obj)</term>

            <listitem>

              <para>Seals an object, and prevents any further 
              modifications of that object. If any property is tried to 
              be modified after it has been sealed, an error is 
              thrown.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefStripTags">

            <term>stripTags(String str) returns String</term>

            <listitem>

              <para>Removes any markup tags contained in the passed 
              string, and returns the modified string.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefWrite">

            <term>write(String str)</term>

            <listitem>

              <para>Writes a string to java.lang.System.out, i.e. to 
              the console. Useful for debugging purposes.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefWriteln">

            <term>writeln(String str)</term>

            <listitem>

              <para>Writes a string together with a line break to 
              java.lang.System.out, i.e. to the console. Useful for 
              debugging purposes.</para>

            </listitem>

          </varlistentry>

        </variablelist>

      </sect3>

      <sect3 id="RefApplicationObject">

        <title>Application Object &apos;app&apos;</title>

        <para>Within the scripting environment Helma provides a host 
        object representing the application itself. This object is 
        always present, and does not need to be instantiated.</para>

        <para>For further details also see the JavaDocs for 
        helma.framework.core. ApplicationBean. Since that class is a 
        JavaBean all get- and set-methods are also directly available 
        as properties of that object.</para>

        <variablelist>

          <varlistentry id="RefAppApp">

            <term>helma.framework.core.Application app.__app__</term>

            <listitem>

              <para>This property contains a reference to Helma&apos;s 
              application class, representing the currently running 
              application, and offers some additional public methods. 
              See Helma&apos;s JavaDocs on 
              helma.framework.core.Application for more 
              information.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetActiveThreads">

            <term>app.getActiveThreads() returns Int</term>

            <listitem>

              <para>Returns the number of currently active threads 
              (=request evaluators).</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetActiveUsers">

            <term>app.getActiveUsers() returns User[]</term>

            <listitem>

              <para>Returns an array of Users, that are currently 
              logged in.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppAddCronJob-1">

            <term>app.addCronJob(String functionName)</term>

            <listitem>

              <para>Adds a global function to the list of CronJobs, 
              that are being called periodically. If the property 
              &apos;schedulerInterval&apos; has not been set otherwise 
              in app.properties, the function will be called every 60 
              seconds.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppAddCronJob-2">

            <term>app.addCronJob(String functionName, String year, 
            String month, String day, String weekday, String hour, 
            String minute)</term>

            <listitem>

              <para>Adds a global function to the list of CronJobs, 
              that are being called periodically. By passing along 
              further arguments it is possible to define at what times 
              that function should be called. The same syntax 
              (&apos;*&apos;, &apos;1,10,15&apos;, &apos;1-5&apos;,..) 
              as for Unix&apos; crontab file can be used. Note, that if 
              the property &apos;schedulerInterval&apos; has been set 
              in app.properties below 60, the function will be called 
              several times in the minute, that it is supposed to 
              run.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetAppDir">

            <term>app.getAppDir() returns String</term>

            <listitem>

              <para>Returns the absolute path to the application 
              directory. For multiple repositories this is either, the 
              directory specified as &apos;appdir&apos; in 
              apps.properties, or the first FileRepository occurring in 
              the repository list.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetCacheUsage">

            <term>app.getCacheUsage() returns Int</term>

            <listitem>

              <para>Returns the number of currently cached objects for 
              the current application.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetCronJobs">

            <term>app.getCronJobs() returns Object</term>

            <listitem>

              <para>Returns a JavaScript object with the function name as property names and the helma.util.CronJob 
instance as property values.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppClearCache">

            <term>app.clearCache()</term>

            <listitem>

              <para>Removes all objects from the object cache for the 
              current application. By calling this method it is 
              possible to force Helma to fetch all objects fresh from 
              the database.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppCountSessions">

            <term>app.countSessions() returns Int</term>

            <listitem>

              <para>Returns the number of currently active 
              sessions.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppCreateSession">

            <term>app.createSession(String sessionID) returns 
            SessionObject</term>

            <listitem>

              <para>Creates a SessionObject with the passed sessionID 
              as its unique identifier. If such a session with that ID 
              already exists, it will return that session.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppData">

            <term>Object app.data</term>

            <listitem>

              <para>This property offers the possibility to store 
              arbitrary data on an application wide level, and is 
              available as long as the application is running. Note, 
              that this can just be used as a temporary storage (i.e. 
              as a &apos;cache&apos;), since this data is not stored 
              persistently within Helma, and is lost when the 
              application is restarted.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppDebug-1">

            <term>app.debug(String str)</term>

            <listitem>

              <para>Writes a string to the eventLog-file, if debug is 
              set to true in app.properties.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppDebug-2">

            <term>app.debug(String logname, String str)</term>

            <listitem>

              <para>Writes a string to a logfile named 
              &apos;logname&apos;, if debug is set to true in 
              app.properties.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetDir">

            <term>app.getDir() returns String</term>

            <listitem>

              <para>Returns the absolute path to the application 
              directory. For multiple repositories this is either, the 
              directory specified as &apos;appdir&apos; in 
              apps.properties, or the first FileRepository occurring in 
              the repository list.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetErrorCount">

            <term>app.getErrorCount() returns Int</term>

            <listitem>

              <para>Returns the number of errors that have occurred 
              since the application has been started.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetFreeThreads">

            <term>app.getFreeThreads() returns Int</term>

            <listitem>

              <para>Returns the number of currently free threads (i.e. 
              request evaluators). This is equivalent to 
              app.getMaxThreads() minus app.getActiveThreads().</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetSession">

            <term>app.getSession(String sessionID) returns 
            SessionObject</term>

            <listitem>

              <para>Returns a SessionObject identified through the 
              passed sessionID, if such a session exists.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetSessions">

            <term>app.getSessions() returns SessionObject[]</term>

            <listitem>

              <para>Returns an array of all currently active sessions, 
              represented as SessionObjects.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetSessionsForUser-1">

            <term>app.getSessionsForUser(String username) returns 
            SessionObject[]</term>

            <listitem>

              <para>DEPRECATED</para>

              <para>Returns an array of all currently active sessions, 
              which have been associated with a certain user, that is 
              identified through the passed username. Returns an empty 
              array if no such User can be found.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetSessionsForUser-2">

            <term>app.getSessionsForUser(User usr) returns 
            SessionObject[]</term>

            <listitem>

              <para>Returns an array of all currently active sessions, 
              which have been associated with the passed User. Returns 
              an empty array if no User is passed.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetSkinfiles">

            <term>app.getSkinfiles() returns Map</term>

            <listitem>

              <para>Returns a Map that allows access to all defined 
              file-based skins. The map contains for each prototype one 
              entry (for prototypes containing also uppercase letters, 
              also an entry with the lowercased prototype name is 
              contained). This entry contains for each skin file 
              residing in that prototype directory, an entry with the 
              name of the file and the content/source of that file as 
              its value.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetUser">

            <term>app.getUser(String username) returns User</term>

            <listitem>

              <para>DEPRECATED</para>

              <para>Returns a User identified through the passed 
              username. The prototype User must have a username defined 
              through the &apos;_name&apos;-property in type.properties 
              for this to work.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppLog-1">

            <term>app.log(String str)</term>

            <listitem>

              <para>Writes a string to the eventLog-file.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppLog-2">

            <term>app.log(String logname, String str)</term>

            <listitem>

              <para>Writes a string to a logfile named 
              &apos;logname&apos;.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppMaxThreads">

            <term>Int app.maxThreads</term>

            <listitem>

              <para>This property contains the maximum number of 
              threads (=request evaluators) that are being created by 
              Helma to handle incoming requests. This property is read- 
              and write-able.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppModules">

            <term>Map app.modules</term>

            <listitem>

              <para>This property offers a dedicated place to store 
              module-related data on an application wide level. Note, 
              that this can just be used as a temporary storage (i.e. 
              as a &apos;cache&apos;), since this data is not stored 
              persistently within Helma, and is lost when the 
              application is restarted.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetName">

            <term>app.getName() returns String</term>

            <listitem>

              <para>Returns the name of the current application, i.e. 
              the name used in apps.properties.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppProperties">

            <term>Map app.properties</term>

            <listitem>

              <para>This property offers access to each defined 
              key/value pair defined in either app.properties or in 
              server.properties. This property is read-only.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetRegisteredUsers">

            <term>app.getRegisteredUsers() returns User[]</term>

            <listitem>

              <para>DEPRECATED</para>

              <para>Returns an array of all created Users.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppRegisterUser">

            <term>app.registerUser(String username, String 
            password)</term>

            <listitem>

              <para>DEPRECATED</para>

              <para>Creates a new HopObject of prototype User, stores 
              it persistently, and associates the current session with 
              that User. The prototype User must have a username 
              defined through the &apos;_name&apos;-property in 
              <filename>type.properties</filename>, and must have a 
              property named &apos;password&apos;.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppRemoveCronJob">

            <term>app.removeCronJob(String functionName)</term>

            <listitem>

              <para>Removes a CronJob, identified through the passed 
              function name, from the list of CronJobs.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetRequestCount">

            <term>app.getRequestCount() returns Int</term>

            <listitem>

              <para>Returns the number of web requests that occurred 
              since the application has been started.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetServerDir">

            <term>app.getServerDir() returns String</term>

            <listitem>

              <para>Returns the absolute path to the home directory of 
              this Helma installation. If Helma is run in embedded 
              mode, this will be equal to the application 
              directory.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetUpSince">

            <term>app.getUpSince() returns Date</term>

            <listitem>

              <para>Returns the timestamp of when that application has 
              been started.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefAppGetXmlrpcCount">

            <term>app.getXmlrpcCount() returns Int</term>

            <listitem>

              <para>Returns the number of XmlRpc requests that occurred 
              since the application has been started.</para>

            </listitem>

          </varlistentry>

        </variablelist>

      </sect3>

      <sect3 id="RefHopObject">

        <title>Hop Object</title>

        <para>A HopObject is an extended JavaScript object, providing 
        the concept of so called collections, i.e. containers of other 
        HopObjects. Each instantiated prototype in Helma is a scripted 
        HopObject, with additional HopObjects for each of its defined 
        collections and mountpoints.</para>

        <para>HopObjects can be in transient state or are persistently 
        mapped on a database. For further details see the according 
        section in the User&apos;s Guide.</para>

        <para>
        <ulink url="http://helma.org/docs/reference/hopobject/  ">http://helma.org/docs/reference/hopobject/</ulink></para>

        <para>see JavaDocs for helma.scripting.rhino.HopObject</para>

        <variablelist>

          <varlistentry id="RefHopObjectStaticGetById">

            <term>HopObject.getById(Int id, String prototypename) 
            return HopObject</term>

            <listitem>

              <para>Fetches a HopObject of a certain prototype through 
              its ID and its prototype name. The prototype name can 
              either be passed as a second argument, or as an 
              alternative, the function can also be called on the 
              prototype itself with a single argument (e.g. 
              Story.getById(123)).</para>

              <para>In case of multiple prototypes being mapped on the 
              same table (which is for instance the case with inherited 
              prototypes) Helma will not check whether the prototype of 
              the fetched object actually matches the specified 
              prototype.</para>

              <para>Note, that this refers to the static method 
              &apos;getById&apos;, not to be mixed up with the method 
              getById called on a specific HopObject.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectAdd">

            <term>obj.add(HopObject child) returns Boolean</term>

            <listitem>

              <para>Adds a HopObject to a collection. If the HopObject, 
              on which that function is called on, is persistent, then 
              the added HopObject will also be made persistent. Returns 
              true in case of success.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectAddAt">

            <term>obj.addAt(Int index, HopObject child) returns 
            Boolean</term>

            <listitem>

              <para>Adds a HopObject to a collection at a certain 
              position, and shifts the index of all succeeding objects 
              in that collection by one. Index positions start with 0. 
              Just makes sense for HopObjects, that are not mapped on a 
              relational DB, since the sort order of the collection 
              would in such a case be defined by type.properties, resp. 
              by the database itself. Returns true in case of 
              success.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectCache">

            <term>HopObject obj.cache</term>

            <listitem>

              <para>This property contains a transient HopObject, which 
              can be used for caching purposes. Information stored in 
              that property will get lost as soon as the application is 
              restarted, or that particular HopObject gets kicked out 
              of Helma&apos;s ObjectCache, or if the method clearCache 
              is being called.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectClearCache">

            <term>obj.clearCache()</term>

            <listitem>

              <para>Removes all information stored in the property 
              obj.cache. Just calling &apos;obj.cache = null&apos; is 
              not possible, since the property itself can not be 
              set.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectContains">

            <term>obj.contains(HopObject obj) returns Int</term>

            <listitem>

              <para>Returns the position index of the passed HopObject 
              in that collection (with 0 being the first element in the 
              collection). If the passed HopObject is not contained in 
              that collection -1 will be returned.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectCount">

            <term>obj.count() returns Int</term>

            <listitem>

              <para>Returns the size of that collection. This method is 
              equivalent to obj.size().</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectGet-1">

            <term>obj.get(Int index) returns HopObject</term>

            <listitem>

              <para>Fetches a HopObject from a collection through its 
              index position. If the passed index is too large, then 
              &apos;null&apos; will be returned. If the passed index is 
              negative, a java.lang.ArrayIndexOutOfBoundsException will 
              be thrown.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectGet-2">

            <term>obj.get(String str) returns HopObject</term>

            <listitem>

              <para>Fetches a HopObject from a collection through its 
              accessname, which needs to be specified for that 
              collection in the according type.properties, and which 
              needs to be a unique identifier in that collection.</para>

              <para>This method just works for persistent HopObjects. 
              If no specific accessname is defined in type.properties, 
              the id will be taken as accessname.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectGetById">

            <term>obj.getById(String str) returns HopObject</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectHref">

            <term>obj.href(String action) returns String</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectInvalidate-1">

            <term>obj.invalidate() returns Boolean</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectInvalidate-2">

            <term>obj.invalidate(String childId) returns Boolean</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectList-1">

            <term>obj.list() returns HopObject[]</term>

            <listitem>

              <para>Returns the contained HopObjects in that collection 
              as an array, with the sort order being preserved. Note, 
              that this function should not be called on very large 
              collections, since on the one hand Helma needs to fetch 
              all contained HopObjects, and on the other hand 
              JavaScript is not well optimized for handling large 
              arrays.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectList-2">

            <term>obj.list(Int start, Int length) returns 
            HopObject[]</term>

            <listitem>

              <para>Returns all contained HopObjects, starting from a 
              certain index position until another certain index 
              position, as an array. Both arguments need to be 
              specified.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectPersist">

            <term>obj.persist() returns Int</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectPrefetchChildren">

            <term>obj.prefetchChildren(Int startIndex, Int 
            length)</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectRemove">

            <term>obj.remove() returns Boolean</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectRemoveChild">

            <term>obj.removeChild(HopObject child) returns 
            Boolean</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectRenderSkin-1">

            <term>obj.renderSkin(SkinObject skin, Object param) returns 
            Boolean</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectRenderSkin-2">

            <term>obj.renderSkin(String skinname, Object param) returns 
            Boolean</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectRenderSkinAsString-1">

            <term>obj.renderSkinAsString(SkinObject skin, Object param) 
            returns String</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectRenderSkinAsString-2">

            <term>obj.renderSkinAsString(String skinname, Object param) 
            returns String</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectSet">

            <term>obj.set(String key, Object value) returns 
            Boolean</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefHopObjectSize">

            <term>obj.size() returns Int</term>

            <listitem>

              <para>Returns the size of that collection. This method is 
              equivalent to obj.size().</para>

            </listitem>

          </varlistentry>

        </variablelist>

      </sect3>

      <sect3 id="RefMimePart">

        <title>MimePart</title>

        <para>see JavaDocs for helma.util.MimePart</para>

        <variablelist>

          <varlistentry id="RefMimePartNew">

            <term>new Packages.helma.util.MimePart(String name, byte[] 
            content, String contentType)</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefMimePartContentLength">

            <term>part.contentLength returns Int</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefMimePartContentType">

            <term>part.contentType returns String</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefMimePartETag">

            <term>part.eTag returns String</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefMimePartGetContent">

            <term>part.getContent() returns byte[]</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefMimePartGetText">

            <term>part.getText() returns String</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefMimePartLastModified">

            <term>part.lastModified returns Date</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefMimePartName">

            <term>part.name returns String</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefMimePartWriteToFile-1">

            <term>part.writeToFile(String dir) returns String</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefMimePartWriteToFile-2">

            <term>part.writeToFile(String dir, String fname) returns 
            String</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

        </variablelist>

      </sect3>

      <sect3 id="RefEventFunctions">

        <title>onXYZ() Event Functions</title>

        <variablelist>

          <varlistentry id="RefOnCodeUpdate">

            <term>onCodeUpdate()</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefOnLogout">

            <term>onLogout()</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefOnRequest">

            <term>onRequest()</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefOnStart">

            <term>onStart()</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

        </variablelist>

      </sect3>

      <sect3 id="RefRequestObject">

        <title>Request Object &apos;req&apos;</title>

        <para>
        <ulink url="http://helma.org/docs/reference/request/">http://helma.org/docs/reference/request/</ulink></para>

        <para>see JavaDocs for helma.framework.RequestBean</para>

        <variablelist>

          <varlistentry id="RefReqAction">

            <term>String req.action</term>

            <listitem>

              <para>Returns the name of the requested action (without 
              the suffix &apos;_action). This property is 
              read-only.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefReqData">

            <term>Object req.data</term>

            <listitem>

              <para>Returns an object containing all passed request 
              parameters as named properties, no matter whether these 
              have been submitted as URL-parameters, as part of a HTTP 
              POST request, or as cookie values.</para>

              <para>If more than one value is submitted for one 
              parameter name, the values are stored inside an array 
              called req.data.paramname_array, that is the _array 
              suffix is added to the parameter name. req.data.paramname 
              itself will just contain a single value.</para>

              <para>Uploaded files are available in req.data as a 
              <link linkend="RefMimePart">MimePart object</link>.</para>

              <para>Additionally Helma sets the following HTTP 
              environment variables if they are available:</para>

              <variablelist>

                <varlistentry>

                  <term>authorization</term>

                  <listitem>

                    <para>Equivalent to the variable 
                    &apos;authorization&apos; sent in the request 
                    header.</para>

                  </listitem>

                </varlistentry>

                <varlistentry>

                  <term>http_browser</term>

                  <listitem>

                    <para>Name and version of the client browser. 
                    Equivalent to the variable &apos;User-Agent&apos; 
                    sent in the request header.</para>

                  </listitem>

                </varlistentry>

                <varlistentry>

                  <term>http_host</term>

                  <listitem>

                    <para>Host name to which that request was sent to. 
                    Equivalent to the variable &apos;Host&apos; sent in 
                    the request header.</para>

                  </listitem>

                </varlistentry>

                <varlistentry>

                  <term>http_language</term>

                  <listitem>

                    <para>Equivalent to the variable 
                    &apos;Accept-Language&apos; sent in the request 
                    header.</para>

                  </listitem>

                </varlistentry>

                <varlistentry>

                  <term>http_language</term>

                  <listitem>

                    <para>Equivalent to the variable 
                    &apos;Accept-Language&apos; sent in the request 
                    header.</para>

                  </listitem>

                </varlistentry>

                <varlistentry>

                  <term>http_remotehost</term>

                  <listitem>

                    <para>IP-Address of the client machine. Equivalent 
                    to a getRemoteAddr() call on the 
                    servletRequest.</para>

                  </listitem>

                </varlistentry>

                <varlistentry>

                  <term>http_referer</term>

                  <listitem>

                    <para>URL of the page the user came from. 
                    Equivalent to the variable &apos;Referer&apos; sent 
                    in the request header.</para>

                  </listitem>

                </varlistentry>

              </variablelist>

              <para>All properties of req.data are read- and 
              write-able.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefReqIsGet">

            <term>req.isGet() returns Boolean</term>

            <listitem>

              <para>Returns true if the current request is a HTTP GET 
              request, false otherwise.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefReqIsPost">

            <term>req.isPost() returns Boolean</term>

            <listitem>

              <para>Returns true if the current request is a HTTP POST 
              request, false otherwise.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefReqGetMethod">

            <term>req.getMethod() returns String</term>

            <listitem>

              <para>Returns the HTTP method (in uppercase letters) of 
              the current request, which is usually &apos;GET&apos; or 
              &apos;POST&apos;. For non-web-requests this function will 
              return on of the following: &apos;XMLRPC&apos;, 
              &apos;EXTERNAL&apos; or &apos;INTERNAL&apos;.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefReqGetPassword">

            <term>req.getPassword() returns String</term>

            <listitem>

              <para>Returns the according decrypted password, if the 
              request contains user credentials sent with &apos;basic 
              authentication scheme&apos; method, typically as a result 
              of a previously returned 401 HTTP response. (LINK: 
              <ulink url="http://en.wikipedia.org/wiki/Basic_authentication_scheme">
              http://en.wikipedia.org/wiki/Basic_authentication_scheme</ulink>)</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefReqPath">

            <term>String req.path</term>

            <listitem>

              <para>Returns the path of the current request, relative 
              to the mountpoint of the current application, and without 
              a preceding slash. This property is read-only.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefReqGetRuntime">

            <term>req.getRuntime() returns Int</term>

            <listitem>

              <para>Returns the amount of time that has elapsed since 
              the start of the processing of the current request, 
              measured in milliseconds.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefReqGetServletRequest">

            <term>req.getServletRequest() returns 
            javax.servlet.http.HttpServletRequest</term>

            <listitem>

              <para>Returns an instance of the Java HttpServletRequest 
              Class corresponding to the current request, which allows 
              full access to the methods of that class.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefReqUsername">

            <term>req.username</term>

            <listitem>

              <para>Returns the according decrypted username, if the 
              request contains user credentials sent with &apos;basic 
              authentication scheme&apos; method, typically as a result 
              of a previously returned 401 HTTP response. (LINK: 
              <ulink url="http://en.wikipedia.org/wiki/Basic_authentication_scheme">
              http://en.wikipedia.org/wiki/Basic_authentication_scheme</ulink>)</para>

            </listitem>

          </varlistentry>

        </variablelist>

      </sect3>

      <sect3 id="RefResponseObject">

        <title>Response Object &apos;res&apos;</title>

        <para>
        <ulink url="http://helma.org/docs/reference/response/">http://helma.org/docs/reference/response/</ulink></para>

        <para>see JavaDocs for helma.framework.ResponseBean</para>

        <variablelist>

          <varlistentry id="RefResAbort">

            <term>res.abort()</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResCache">

            <term>res.cache returns Boolean</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResCharset">

            <term>res.charset</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResCommit">

            <term>res.commit()</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResContentType">

            <term>res.contentType returns String</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResData">

            <term>res.data</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResDebug">

            <term>res.debug(String)</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResDependsOn">

            <term>res.dependsOn(String)</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResDigest">

            <term>res.digest()</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResEncode">

            <term>res.encode(String)</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResEncodeXml">

            <term>res.encodeXml(String)</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResError">

            <term>res.error</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResEtag">

            <term>res.etag</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResFormat">

            <term>res.format(String)</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResForward">

            <term>res.forward(String url)</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResHandlers">

            <term>res.handlers</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResLastModified">

            <term>res.lastModified returns Date</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResMessage">

            <term>res.message</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResMeta">

            <term>res.meta</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResPop">

            <term>res.pop() returns String</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResPush">

            <term>res.push()</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResRealm">

            <term>res.realm</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResRedirect">

            <term>res.redirect(String)</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResReset">

            <term>res.reset()</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResServletResponse">

            <term>res.servletResponse returns 
            javax.servlet.http.HttpServletResponse</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResSetCookie">

            <term>res.setCookie(String key, String value, Int days, 
            String path, String domain)</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResSkinpath">

            <term>res.skinpath</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResStatus">

            <term>res.status</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResWrite">

            <term>res.write(String str)</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResWriteBinary">

            <term>res.writeBinary(ByteArray bytes)</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefResWriteln">

            <term>res.writeln(String str)</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

        </variablelist>

      </sect3>

      <sect3 id="RefSessionObject">

        <title>SessionObejct &apos;session&apos;</title>

        <para>Each web request is associated with a SessionObject 
        representing a &apos;user session&apos;. Helma recognises 
        requests being made from the same client within the same 
        session through a session cookie named &apos;HopSession&apos;. 
        If no such cookie is sent with the request, Helma will set that 
        a cookie with a random hash with the next response.</para>

        <para>Within the scripting environment &apos;session&apos; 
        always represent the current session of the user, who initiated 
        the web request. Furthermore it is also possible to fetch 
        active sessions of other clients through the method 
        app.getSessions(), and to artificially create SessionObjects 
        through app.createSession().</para>

        <para>For further details also see the JavaDocs for 
        helma.framework.core.SessionBean. Since that class is a 
        JavaBean all get- and set-methods are also directly available 
        as properties of that object.</para>

        <variablelist>

          <varlistentry id="RefSessionID">

            <term>String session._id</term>

            <listitem>

              <para>Contains the unique identifier of the current 
              session, which is equivalent to the value stored in the 
              HopSession-cookie on the client side. This property is 
              read-only.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSessionCookir">

            <term>String session.cookie</term>

            <listitem>

              <para>Contains the unique identifier of the current 
              session, which is equivalent to the value stored in the 
              HopSession-cookie on the client side. This property is 
              read-only.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSessionData">

            <term>HopObject session.data</term>

            <listitem>

              <para>This property of the SessionObject offers the 
              possibility to store arbitrary data within the current 
              user session. Note, that this can just be used as a 
              temporary storage, since sessions are not stored 
              persistently within Helma, and are generally lost when 
              the application is restarted.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSessionLastActive">

            <term>Date session.lastActive</term>

            <listitem>

              <para>Contains the timestamp of the last web request, 
              that has been submitted by that client. This property is 
              read-only, but can be set to the current time through 
              session.touch().</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSessionLastModified">

            <term>Date session.lastModified</term>

            <listitem>

              <para>Contains the timestamp of when the associated 
              client started the session, or logged in, or logged out 
              the last time, whichever happened most recently. This 
              property is read- and write-able.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSessionMessage">

            <term>String session.message</term>

            <listitem>

              <para>??? -&gt; HANNES</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSessionOnSince">

            <term>Date session.onSince</term>

            <listitem>

              <para>Contains the timestamp of when the client started 
              the session. This property is read-only.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSessionUser">

            <term>User session.user</term>

            <listitem>

              <para>Contains a reference to the UserObject associated 
              with this session. This property is null if the client 
              has not been logged in yet, or has already been logged 
              out. Checking this for being unequal to null is the usual 
              way to check whether a client is logged in or not. This 
              property is read-only, but can be set through the method 
              session.login(User usr).</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSessionLastActiveFn">

            <term>session.lastActive() returns Date</term>

            <listitem>

              <para>Returns the timestamp of the last web request, that 
              has been submitted by that client.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSessionLogin-1">

            <term>session.login(User usr)</term>

            <listitem>

              <para>Associates the passed User to that session, i.e. 
              logs the client in as that User. The property user of the 
              session object will refer to the User.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSessionLogin-2">

            <term>session.login(String username, String password) 
            returns Boolean</term>

            <listitem>

              <para>DEPRECATED</para>

              <para>Fetches a User via the passed username (which is 
              defined through _name in User/type.properties, and which 
              needs to be unique), checks whether the passed password 
              matches the password of that User (the password property 
              actually needs to be named &apos;password&apos; in 
              User/type.properties), and in case of a match, actually 
              associates that session with that User. The property user 
              of the session object will then refer to the User.</para>

              <para>If either such a User is not found, or the password 
              does not match the method will return false.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSessionLogout">

            <term>session.logout()</term>

            <listitem>

              <para>Logs out the current session, i.e. removes the 
              reference to a User if one exists. Additionally the 
              global function onLogout will be called.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSessionOnSinceFn">

            <term>session.onSince() returns Date</term>

            <listitem>

              <para>Returns the timestamp of when the client started 
              the session.</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSessionTouch">

            <term>session.touch()</term>

            <listitem>

              <para>This will set the lastActive property to the 
              current timestamp, and can be used to artificially avoid 
              a session timeout.</para>

            </listitem>

          </varlistentry>

        </variablelist>

      </sect3>

      <sect3 id="RefSkinObject">

        <title>Skin Object</title>

        <para>
        <ulink url="http://helma.org/docs/reference/skin/">http://helma.org/docs/reference/skin/</ulink></para>

        <para>see JavaDocs for helma.framework.core.Skin</para>

        <variablelist>

          <varlistentry id="RefSkinAllowMacro">

            <term>sk.allowMacro(String macroname)</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSkinContainsMacro">

            <term>sk.containsMacro(String macroname) returns 
            Boolean</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

          <varlistentry id="RefSkinGetSource">

            <term>sk.getSource() returns String</term>

            <listitem>

              <para>FIXME</para>

            </listitem>

          </varlistentry>

        </variablelist>

      </sect3>

    </sect2>

  </sect1>

  <sect1 id="Tutorials">

    <title>Tutorials</title>

    <sect2 id="TutorialHelloWorld">

      <title>Hello World</title>

      <para>This is the most basic version of a tutorial. It will guide 
      you to writing your own &apos;Hello 
      World&apos;-application.</para>

      <para>If you haven&apos;t setup Helma yet, then go ahead and 
      download a recent Helma package from 
      <ulink url="http://helma.org/download/">
      http://helma.org/download/</ulink>, unzip it anywhere on your 
      disk and start Helma by double-clicking on the <command>
      hop.bat</command>, resp. <command>hop.sh</command> residing in 
      the unzipped directory (to which we will refer as <filename>
      [HelmaHome]</filename> from now on).</para>

      <para>Now, open the file <filename>
      [HelmaHome]/apps.properties</filename> with your favourite text 
      editor, and add the following line:</para>

      <programlisting>helloworld</programlisting>
      <para>As soon as you save your changes to disk, Helma will 
      automatically detect these changes, will create a directory named 
      <filename>[HelmaHome]/apps/helloworld</filename> (with several 
      subdirectories) and will activate this application.</para>

      <para>Now change to the directory <filename>
      [HelmaHome]/apps/helloworld/Root</filename> and save the 
      following JavaScript-code as a new text-file named 
      &apos;functions.js&apos; (again with your preferred 
      editor).</para>

      <programlisting>function main_action() { 
  res.write(&quot;Hello World!&quot;); 
}</programlisting>
      <para>And that&apos;s already it. We just created a new web 
      accessible function named &apos;main&apos; for the Root object. 
      So, go ahead, open up 
      <ulink url="http://localhost:8080/helloworld/main">
      http://localhost:8080/helloworld/main</ulink> with your web 
      browser of choice, and you will see the &quot;Hello World&quot; 
      message being displayed to you.</para>

      <para>Some explanations:</para>

      <itemizedlist>

        <listitem>

          <para>The Root object is always there in Helma, so we 
          didn&apos;t had to create it explicitly.</para>

        </listitem>

        <listitem>

          <para>By having our defined function end with 
          &apos;_action&apos; we told Helma, that this particular 
          function should be callable for browsers.</para>

        </listitem>

        <listitem>

          <para>The single line of code in that function simply appends 
          the string &quot;Hello World&quot; to the response.</para>

        </listitem>

      </itemizedlist>

    </sect2>

    <sect2 id="TutorialAddressbook">

      <title>Address book</title>

      <para>TODO. See <ulink url="http://helma.org/docs/tutorial/">
      http://helma.org/docs/tutorial/</ulink> for the moment.</para>

    </sect2>

    <sect2 id="TutorialMailClient">

      <title>Mail Client</title>

      <para>TODO. See the source for the time being.</para>

    </sect2>

  </sect1>

</article>


