Goanna HTML Web Application Framework


The Goanna Web Application Framework provides a complete open source solution for developing cross platform cross compiler web based applications in Eiffel. The major features of the library include:

Current Status

The Goanna Web Application Framework is considered Beta software. As of mid September, 2005, it has been in production use at a low volume Internet facing website for over one year. The goa_build application has undergone much less usage. In particular, goa_build error handling is largely untested.

Building the Development Environment

  1. Install the Subversion version of the GOBO library as described here taking care to set all of the required environment variables and bootstrap the library.
  2. Install version 2.2 of the e-POSIX Library taking care to set all of the required environment variables.
  3. Install the SVN version of Goanna as described here taking care to set the GOANNA environment variable.
  4. Install FastCgi on the web server that will be hosting the application. This step is not necessary if you will be configuring the application as a stand alone web server, but Goanna's stand alone web application facility is not recommended for production use.

    A sample Apache httpd.conf file is provided in $GOANNA/examples/application. Each Goanna Application running on the web server must have it's own FastCgiExternalServer with a unique (developer selected) path and it's own port. The port number must match APPLICATION_CONFIGURATION.port. In each alias declaration, the alias path is selected by the developer and the path in quotes must match the path given in the FastCgiExternalServer declaration. You may then access the application at the following URL:

    http://hostname/alias/subdirectory/go_to.htm?page=servlet (without the extension)

    Where 'alias' is the developer specified alias in httpd.conf and 'subdirectory' is any developer selected string. The 'subdirectory' is not configured within the application as it will respond to requests for any subdirectory under 'alias'.

  5. Compile the goa_build tool:

    cd $GOANNA/goa_build

    geant compile

    Then place the executable goa_build in your system's execution path.

  6. If you will be doing custom HTML generation, install trang and ensure the trang executable is in your system's execution path or set the environment variable TRANG_INVOCATION with the complete path to the trang executable including the executable file name. For example:

    TRANG_INVOCATION=$JAVA_HOME/bin/java -jar /usr/local/share/trang/trang.jar

Application Development

Creating an Application

An example application is available in:

$GOANNA/examples/application

Click here to see the demo in action.

  1. Create a new project directory and copy all of the files (and subdirectories) in $GOANNA/library/application/templates to your project directory and then delete the CVS subdirectories (recursively).

    In our example, we will create the directory "application".

  2. If desired, search and replace "web_application" with your project name in build.eant and make any necessary additions to system.xace.
  3. APPLICATION_SERVER must inherit from exactly one servlet connector. We recommend using GOA_FAST_CGI_SERVLET_APP, but his requires that FastCGI has been installed on the web server. If this is not available, then GOA_CGI_SERVLET_APP can be used. Alternatively, GOA_HTTPD_SERVLET_APP can be used, thus avoiding the need for a web server altogether. But this is only recommended for testing. You might also consider writing your own servlet connector, if you have a special environment. If you use the FastCGI connector, you must undefine GOA_FAST_CGI_SERVLET_APP.initialise_logger, and GOA_FAST_CGI_SERVLET_APP.all_servlets_registered. If you use the stand alone connector you must undefine log_hierarchy.

    In our example, we have used GOA_FAST_CGI_SERVLET_APP.

  4. PARAMETER_PROCESSING_RESULT, APPLICATION_CONFIGURATION, and all servlets must inherit from GOA_NON_DATABASE_ACCESS_TRANSACTION_MANAGEMENT or another (developer provided) class implementing GOA_TRANSACTION_MANAGEMENT.

    In our example, these classes all inherit from GOA_NON_DATABASE_ACCESS_TRANSACTION_MANAGEMENT.

  5. Implement deferred features. APPLICATION_SERVER.command_line_ok must:

    • First create (or assign an existing object to) APPLICATION_SERVER.active_configuration and then call APPLICATION_SERVER.touch_configuration
    • Create at least one VIRTUAL_DOMAIN_HOST and then call APPLICATION_SERVER.register_virtual_domain_host to register the host under the name given in APPLICATION_CONFIGURATION.default_virtual_domain_host_lookup_string.
    • Return a result of True.

    In our example, this is done in APPLICATION_SERVER.

    The feature APPLICATION_CONFIGURATION.next_page returns the next page to display to the user. In our example, it is APPLICATION_CONFIGURATION.

  6. Switch to the data directory (as defined in APPLICATION_CONFIGURATION and type:

    goa_build

    This will copy Goanna provided rnc (schema) and xsl (transformation) files to the data directory where they can accessed by the application.

Application Servlets

Request processing and page generation within a Goanna Web Application is performed by servlets. Servlets that display information to the user should inherit from GOA_DISPLAYABLE_SERVLET. Servlets that process incoming requests but never generate a response to the user may inherit directly from GOA_APPLICATION_SERVLET. Class QUESTION_SERVLET provides an example of a simple servlet that displays an input form to the user and processes the request when the user submits the form to the server. Developers must implement the deferred feature name which generally should include an extension (e.g. .htm) and is used as the file name portion of the url referring to the servlet. Servlet names must be unique within the system. Servlets must be instantiated (and accessed) as once functions in SHARED_SERVLETS. Additionally, each servlet must be registered by adding a call to GOA_APPLICATION_SERVER.register_servlet (servlet_feature_name_from_SHARED_SERVLETS) within the developer implemented feature APPLICATION_SERVER.register_servlets.

The parameter semantics for each servlet are defined in that servlet's creation instruction by adding the name of each parameter used by the servlet to one of five lists:

A servlet's creation routine may set the features GOA_APPLICATION_SERVLET.receive_secure and GOA_DISPLAYABLE_SERVLET.send_secure to True when required. receive_secure ensures that url's referring to the servlet will use the https protocol. send_secure will ensure that html generated by the servlet is only sent on a secure connection; the client will automatically be redirected to a secure page if necessary.

Developers may redefine the features GOA_APPLICATION_SERVLET.ok_to_process and GOA_DISPLAYABLE_SERVLET.ok_to_display. ok_to_process allows the developer to prevent processing of a form unless the application is in an acceptable state (e.g. user logged in). ok_to_display is intended to prevent sending a particular response unless the application is in an acceptable state, but it is the developer's responsibility to check ok_to_display when implementing APPLICATION_CONFIGURATION.next_page.

Request Parameters

Each parameter submitted in an HTML form is processed by an object of type GOA_DEFERRED_PARAMETER. These objects must be instantiated (and accessed) as once functions in SHARED_REQUEST_PARAMETERS. The same parameter may be used by multiple servlets. In the sample application, NAME_PARAMETER provides an example of a parameter that allows the user to enter their name and stores their input in the sample application's SESSION_STATUS object. It will display an error message to the user if the user attempts to enter an empty name. If the user attempts to replace a previously valid name with an empty name, it will not do the update and will so inform the user. These behaviors are inherited from NON_EMPTY_UPDATE_INPUT_PARAMETER.

The built in parameter classes provided in Goanna are intended as mixin classes. In general, simply inherit from the class or classes that provide(s) the functionality required and then implement the deferred features. Each parameter must be given a unique name. Each parameter should inherit from GOA_NON_DATABASE_ACCESS_TRANSACTION_MANAGEMENT or a developer provided class that implements the features of GOA_TRANSACTION_MANAGEMENT.

Advanced Form Processing

The results of processing a single parameter are stored in an object of type PARAMETER_PROCESSING_RESULT. Processing a request from the user generates an object of type REQUEST_PROCESSING_RESULT which must inherit from their corresponding GOA_ prefixed classes..

Sometimes the processing performed by one parameter is dependent on the processing results from another parameter. Parameters may redefine the feature GOA_DEFERRED_PARAMETER.processing_order to control the order in which the parameters are processed. Legal values range from process_first through process_fifth with a default value of process_third. It is recommended that the developer define invariants specifying the processing order (e.g. parameter_foo.processing_order < parameter_bar.processing_order). The PARAMETER_PROCESSING_RESULT generated by another parameter (within the same request) may be obtained using the query GOA_REQUEST_PROCESSING_RESULT.parameter_processing_result.

Features to store intermediate processing results which are not part of the persistent object model may be added to classes PARAMETER_PROCESSING_RESULT, REQUEST_PROCESSING_RESULT or SESSION_STATUS as appropriate. Hooks to the persistent object model and non persistent information that must live between requests should be placed in SESSION_STATUS. Non-persistent information that must be passed between parameters during processing of a single request may be stored in REQUEST_PROCESSING_RESULT. It is also convenient to place hooks to commonly accessed portions of the persistent object model in REQUEST_PROCESSING_RESULT.

Servlets that must perform additional processing may also redefine the following features:

HTML Generation

Content intended for display to the user is first generated in XML and subsequently transformed to HTML using XSLT. Four kinds of classes are involved in XML generation. XML generation classes are provided with the framework or developers may create a custom document schema. The XML generation class names are prefixed by the name (namespace prefix) of the schema that describes their XML content.

_XML_DOCUMENT, _PREFIX_ATTRIBUTE_VALUES, and _SCHEMA_CODES classes are all generated by the goa_build application from an XML schema. Developers should avoid creating their own classes with these names. The _XML_DOCUMENT_EXTENDED classes are necessary to allow developers to add functionality to a _XML_DOCUMENT without having that code overwritten upon subsequent invocations of the goa_build application.

Generating HTML output for the user may be performed using an GOA_PAGE_XML_DOCUMENT_EXTENDED object. GOA_REDIRECT_XML_DOCUMENT_EXTENDED may be used to redirect the user to another page. GOA_PAGE_XML_DOCUMENT_EXTENDED includes primitives for generating most HTML constructs and a number of convenience features. Additionally, form parameters may be easily added to the document by first calling start_data_entry_table_element and then adding each parameter with add_standard_input_row. A data entry table consists of three columns. Generally, the first column contains the label, the second column the input, and the third column any error messages associated with the parameter. For checkbox and radio button parameter the first two columns are combined and contain the parameter followed by the label.

When adding a hyperlink to a servlet to an XML page, use:

GOA_PAGE_XML_DOCUMENT_EXTENDED.add_item (servlet.hyperlink (processing_result, text)

Referring to the servlet's URL directly will cause the servlet to try and process incoming content. Because no content is included with the URL, this will generally not produce the desired results. If the developer does want the servlet to process the incoming request, use post_url instead (taking care to add the appropriate parameter values to the hyperlink before adding it to the XML document).

The class MESSAGE_CATALOG is intended to contain all text displayed to the user. This design is intended facilitate adding support for multiple languages in the future. It is not strictly necessary for the developer to use MESSAGE_CATALOG but some GOA_REQUEST_PARAMETER classes do generate messages contained in GOA_MESSAGE_CATALOG.

Custom HTML Generation

Goanna Framework Applications generate XML which is then transformed to HTML using XSLT. Developers can extend or replace the support classes used for this purpose. Document schemas produce Eiffel objects that may be instantiated and used within an application. Included schemas produce deferred classes and are intended to be incorporated into document schemas or other included schemas.

Both document and included schemas are specified using Relax NG Compact Syntax (RNC). Each schema must be given a unique namespace and prefix and the namespace prefix must match the file name (excluding the extension of course). Each schema must have a matching XSLT transform that generates HTML given an XML document that conforms with that schema. The document schema and XSLT transform must have the same file name. A document schema and all supporting files (included schemas and XSLT transforms) must be in a single subdirectory. When designing a schema please keep in mind that the full RNC is not currently supported. A list of known limitations may be found here.

To get started on a custom schema:

  1. Create a Relax NG Compact Syntax schema (with a rnc extension) for the XML which your web application will generate.
  2. Create a XSLT stylesheet (with an xsl extension) which will transform that XML into HTML (or any other form required).
  3. Create the CSS stylesheet the HTML page will reference (optional).
  4. Place these files in a separate directory and then type:

    goa_build -f your_schema.rnc

    The files your_schema_xml_document.e, your_schema_attribute_values.e, and your_schema_schema_codes.e will be created in this directory. Create the class YOUR_SCHEMA_XML_DOCUMENT_EXTENDED (with the file name your_schema_xml_document_extended.e) which inherits from YOUR_SCHEMA_XML_DOCUMENT. Use this class in your application.

  5. Additional details and requirements for these files are provided below.

Creating Document Schemas

goa_page.rnc and the corresponding XSLT transform goa_page.xsl provide an example of how to specify a document schema. The document schema must include the namespace declaration for every included schema (recursively), and (generally):

namespace datatypeLibrary ="http://www.w3.org/2001/XMLSchema-datatypes"

The schema must include RNC grammar and start element declarations. Included schemas may be incorporated using the RNC include "filename" declaration. If the developer will use the Goanna library GOA_REQUEST_PARAMETER objects, then the schema (or an included schema) must include the goa_common.rnc schema.

The goa_build program can automatically create an attribute value declaration for the attribute named "class" from a CSS stylesheet and insert it into a document schema. To use this facility, add a stylesheet declaration as a comment to the schema, using the syntax:

# stylesheet="file_name.css"

The stylesheet does not need to be in the same directory as the schema, and it is the developers responsibility to ensure that the stylesheet is also placed in a directory where it can be served by the web server. The stylesheet must include at least one style declaration for every CSS class used in the schema and all included schemas (recursively). If more than one stylesheet are associated with a schema in practice, only one stylesheet is specified in the schema and it must include the superset of classes used with the schema. If the document schema includes (recursively) the goa_common.rnc schema, then a stylesheet declaration is mandatory and the stylesheet must include at least those classes declared in goa_common.css.

If a stylesheet declaration is used, the RNC schema must also include:

CLASS_ATTRIBUTE_PLACEHOLDER

at a location in the schema where it is legal to include a RNC attribute declaration. The placeholder will be replaced with the class attribute declaration. For example:

class = attribute class { "class_name_1" | "class_name_2" }

The XSLT transform corresponding to a document schema must include the XSLT transform corresponding to every included schema. For example:

<xsl:include href="goa_common.xsl" />

If the document schema includes (recursively) goa_common.rnc, then (in order to provide support for Javascripts provided with the framework) the XSLT transform should also include the following statement within the template generating the HTML "HEAD" portion of the document:

<xsl:call-template name="goa_common_scripts" />

The selections for elements and attributes in included schemas that are defined as a choice (.e.g. elem1 = element ns:elem1 { elem2 | elem3 }) may be expanded by using the rnc combine operator |=.

When the new document schema is ready, invoke goa_build from the directory containing the schema and all supporting files. Type:

goa_build -h

for more information on using the boa_build tool. Note that when using the -d --datadirectory switch (recommended) the data directory value given to goa_build -h should match the directory specified in APPLICATION_CONFIGURATION.data_directory. When using the -e --eiffeldirectory (recommended) the directory given should be a valid cluster location within the Eiffel project. The build tool will:

The developer must create a class NAMESPACE_PREFIX_XML_DOCUMENT_EXTENDED which inherits from NAMESPACE_PREFIX_XML_DOCUMENT. The developer may add any additional XML generation features to this class and should generally use the EXTENDED version of an XML document within the application.

Creating Included Schemas

goa_common.rnc and the corresponding XSLT transform goa_common.xsl provide an example of how to specify an included schema. Included schemas must include a namespace declaration (with the namespace prefix matching the file name) and the namespace declaration for each included schema. Included schemas must NOT include the RNC grammar and start element declarations. Included schemas should include a stylesheet declaration if the developers require access to class_attribute_value constants. The stylesheet should contain only those classes which are unique to the schema and not those that are inherited from other included schemas. Included schemas should NOT include a CLASS_ATTRIBUTE_PLACEHOLDER. The developer must also create a file NAMESPACE_PREFIX_XML_DOCUMENT_EXTENDED which inherits from NAMESPACE_PREFIX_XML_DOCUMENT for included schemas.

When processing included schemas, the goa_build tool will generate the deferred class NAMESPACE_PREFIX_XML_DOCUMENT. The tool will also generate the (effective) class NAMESPACE_PREFIX_ATTRIBUTE_VALUES which will contain string constants for all attributes declared as a choice of string values. However, class attribute value constants will NOT be included in NAMESPACE_PREFIX_ATTRIBUTE_VALUES. Instead, deferred features for each class name present the stylesheet will be added to the class NAMESPACE_PREFIX_XML_DOCUMENT.


Copyright 2007, Neal L. Lester and others
mailto:neallester@users.sourceforge.net
http://goanna.sourceforge.net
Last Updated: June 28, 2007