WebDisplay Documentation

Table of contents

1 Introduction/Overview

1.1 External view/Environment
1.2 Internal view of COAST
1.3 Initial request
1.4 ‚Normal‘ request
1.5 Constructing Pages

2 Server Framework

2.1 Class Server
2.2 Class Session
2.3 Class Role
2.4 Class Page
2.5 Class Context
2.6 Class Action
2.7 Class Reply
2.8 Class Renderer
2.8.1 Class StringRenderer (String)
2.8.2 Class
ImageRenderer (Image)
2.8.3 Class
DateRenderer (Date)
2.8.4 Class
ContextLookupRenderer (ContextLookup)
2.8.5 Class
ContextLookupRenderer (Render)
2.8.6 Class
AppletRenderer (Applet)
2.8.7 Class
TableRenderer (Table) and ItemRenderer (Item)
2.8.8 Class
LinkRenderer (Link)
2.8.9 Class
HTMLTemplateRenderer (HTML)
2.8.10 Class
FormRenderer (Form) and FieldRenderer (Field)
2.8.11 Class
ConditionalRenderer (Condition)
2.8.12 Class
SwitchRenderer (Switch)
2.8.13 Class
URLRenderer (URL)
2.8.14 Class
TextAreaRenderer (TextArea)

3 Infrastructure Classes

    3.1 Class HashTable
    3.2 Class Registry
    3.3 Class Thread
    3.4 Class Mutex and LockUnlockEntry
    3.5 Class Condition
    3.6 Class String
    3.7 Class Stream
    3.8 Class Anything: A Dynamic Data Structure
    3.8.1
    Import/Export format of Anythings
    3.8.2 Anythings as a Configuration Mechanism
    3.8.3 Usage of Anythings in COAST

    3.8.3.1 Global configuration
    3.8.3.2 Debugging subsystem
    3.8.3.3 Pages
    3.8.3.4 Renderers
    3.8.3.5 Roles
    3.8.3.6 Actions
    3.8.3.7 Searchable flexible Storage
    3.8.3.8 Parameters
    3.9 Class System
    3.10 Class Tracer

     


1. Introduction/Overview

    COAST is a platform for developing and deploying World Wide Web applications. For development, it provides an extensible framework, reusable components, and a configuration mechanism that is used to create web applications. For deployment, it provides an efficient and scaleable server and additional flexible communication infrastructure.

1.1.  External view/Environment

    The following sequence chart illustrates the outside view of the COAST environment:

[[#wd ContextLookup OutsideView ]]

  • creating various HTML components and templates
  • configuring other already existing components by writing configuration files
  • extending a framework based architecture by programming.

 


1.2.   Internal view of COAST

1.3. Initial request

1.4. ‚Normal‘ request

1.5. Constructing Pages

The last phase of processing a request is to generate a page – typically in HTML format – and sending it back to the user’s Web browser.

The important classes of this phase are:

Class Name

Description

Page

renders a page and handles actions

Renderer

render an HTML construct

Reply

collects header and contents of a page

Context

Controls access to application and configuration data

Generating HTML within the Render method of a page is substantially simplified by an abstraction called Renderer. Its RenderAll method generates a piece oft HTML code into a Reply object. A context object is passed in as an argument in order to let the Renderer get access to the session and role state, the page configuration data and the temporary storage of this request.

Simple renderers typically generate HTML for a single UI component, e.g. a string, an image or a field within a form. Composite renderers combine other renderers in useful ways: e.g. applying a layout or conditionally selecting one from a set of many renderers. Most renderers can be configured in a very flexible way. In addition it is very easy to write new renderers for specific tasks.

Renderers are shared singelton objects, they are installed in a registry at startup time and can be referred to by name.

The default implementation of Page::Render uses a configurable name to look up a Renderer by name. Because most pages can be generated by combining predefined and custom made renderers recursively, there is hardly any need for overriding Page::Render.

Chapter 2.8 gives an overview of all available renderers:

 


2. Server Framework

    This chapter lists the purpose and important notes for every class.

2.1. Class Server

Every COAST application is controlled by a single Server object (typically a subclass of Server). This object is most easily allocated in the application’s main() function:

int main(int argc, char **argv)
{
    Anything config;
    Server::StdInit(config);

    return SubClassOfServer(config).Run();
}

The constructor requires an Anything containing the configuration for the server. The static convenience method StdInit() locates the configuration file ("Config.any") and initializes the constructor argument and various other data structures which are necessary before constructing the server object. Later it is possible to use method Lookup()to access elements of the server configuration by name. This can be used to get easy access to configuration information. Typically Lookup is not called directly on a server object but through the chained lookup mechanism of a Context object (chapter 2.5). StdInit()reads the environment variable WD_ROOT and sets the path list defined in "Config.any" (/PathList). If WD_ROOT is not defined relative paths are used.

The Run method of a Server object controls a COAST server and accepts new connections on a socket port (defined in Config.any). For each connection Server tries to find an existing Session object (LookupSession) or creates a new one (CreateSession).

In the rare case where you need to create an application specific subclass of Session you need to subclass class Server as well and override its factory method DoCreateSession to return an instance of your specific subclass. The initial request and environment coming from wdgateway is parsed and passed as an Anything argument to method DoCreateSession.

 

The incoming requests on the specified socket port have the following syntax structure:

{
    /Env { # selected environment arguments
        /REMOTE_HOST "tcp/ip address of client"
        /HTTP_USER_AGENT "type of browser"
        /REQUEST_METHOD "GET or POST"
        /REMOTE_ADDR "tcp/ip address of client"
        /argv0 "name of program"
    }
    /Query {
        /adr "tcp/ip address of COAST"
        /port "port number"
        /sessionId "session id"
    }
}

The /Env part is used to initialize a session object with information like HTTP_USER_AGENT or REMOTE_ADDR. It can also be used to determine the language in which a page shall be rendered.

The sessionId is used to assign the request to an existing Session object. If it cannot be found or if sessionId is not available, the factory method Session::DoCreateSession is called to create a new Session object.

Other methods of Server provide administration functionality and are used by actions valid for an administrator role. These are:

  • CleanShutdown() waits until all active sessions have terminated. Then it shuts down the server process. During the execution of a CleanShutdown request clients cannot initiate new sessions.
  • Abort() waits until all outstanding requests have been processed. No new requests can be processed. Then Abort() shuts down the server.
  • DisableConnection() temporarily disables connections without killing sessions. Clients will see an error message but they can continue a session after an
  • EnableConnection() request has been issued.
  • Reset() waits until all unfinished requests have been processed (like Abort). Then Reset() shuts down the server and restarts it again.

 


2.2 Class Session

A Session object maintains the state of a Session and implements the page transition mechanism within RenderNextPage.

Because Session implements the default behavior for typical web applications. It is usually not necessary to subclass Session.

The constructor (typically called in Server::DoCreateSession()) initializes the session object with a name. The real initialization is done in Session::Init. The framework calls this method after creating new Session objects in Server::DoCreateSession. It provides the unique id for the session object. This id is used to lookup sessions by id while processing requests.

If your application needs specific initialization for its session objects you can either implement this in the session’s constructor or by overriding the (polymorphic) Init method. Don’t forget to call the inherited Init!

Every session maintains a current (non-shared) Role object (fRole). Class Session delegates all decisions which are dependent on the user’s role to the Role object. A Role object implements these policies and provides a role-specific store. The factory method DoCreateDefaultRole of Session can be overridden to return a specific default role for the session. By default DoCreateDefaultRole tries to create a role with name "Default". This role can be easily configured in the registry of role prototypes, which should be the preferred method.

Switching to a new role (method SetRole) frees the old role object and its associated store.

Session state can be maintained in two different places:

  • If the state is closely associated to a role it should be kept in the store of the Role object (see chapter 2.3).
  • If the state should be kept across role changes (e.g. from role "Guest" to role "Customer"), it can be kept in the store of the Session object (access-method GetStore(), returns an Anything).

Lookup() looks up an Anything by name in the session’s data store. If it cannot be found it calls Lookup() on the server object. Typically Lookup() is not called directly on a session object but through the lookup mechanism starting with a Context object (see chapter 2.5).

The method RenderNextPage is the "workhorse" of class Session. RenderNextPage processes an HTTP request and writes a new page (typically in HTML format) into a Reply object. RenderNextPage is called by the server object after a session object has been found or a new one was created.

RenderNextPage has four phases:

  • Finish last page:
    RenderNextPage determines which page contained the URL (except for the initial request) which may typed or come from a regular HTML-page. If a page has been found its Finish-method is called which gives the page object the possibility to post process the request. If a last page cannot be determined (e.g. because its the first request for this session), finishing of the last page is skipped
  • Check whether a role exchange is necessary:
    The action field is extracted from the request and passed to method CheckRoleExchange. The default implementation of CheckRoleExchange determines whether the action triggers a role exchange by using the action as a key into the table "RoleChanges" in the main configuration file (Config.any). If an entry can be found its value is the name of the new role. A new role with that name is created and established in the session. If a role exchange is not necessary the session’s role remains unchanged.
  • Use role to determine next page:
    The current role is used to determine the next page by calling its
    GetNewPage method. The old page (if any) and the current action are passed as arguments.
  • Start new page:
    The new page name is used to lookup the page from the page registry. If successful its Start method is called. Start prepares the page for rendering and eventually renders the page into a Reply object.

 


2.3. Class Role

      A role object implements all decisions which are dependent on the user’s role and provides a role specific data store (an Anything) across requests.

      Role's most important method GetNewPage() implements a state machine which returns the name of the next page based on the current page and some input action.

      The default implementation of GetNewPage() uses a simple table-driven state machine which is initialized from the role’s configuration file (method LoadConfig()). The name of this file is by convention "rolename.any" (e.g. Admin.any). The map lists the name of the resulting page for all possible combinations of pages and actions. An optional entry /Default lists actions that are valid for all pages.

      The following Anything is an example for a state map:

      /Map {
          /Default {
              /Help    "HelpPage"
          }
          /Page1 {
              /Next    "Page2"
          }
          /Page2 {
              /Next     "Page3"
              /Previous "Page1"
          }
          /Page3 {
              /Previous "Page2"
          }
      }

      If GetNewPage is called for an action "Next" on page "Page1" it will return "Page2". If it is called for an action "Next" on page "Page2" it will return "Page3". If it is called with action "Help" for any page, it will return "HelpPage".

      You can subclass Role in order to provide a more sophisticated implementation for the state machine in GetNewPage().

      Subclasses of roles must be registered in a role registry by using the macro RegisterRole(name) in the implementation of the class and passing the role’s class name. Role aliases can be defined in the main COAST configuration file (Config.any). The format of the associated Anything is:

      /Roles {
          /Role1 { "Alias11" "Alias12" … }
          /Role2 { "Alias21" "Alias22" … }
      }

      Role1 and Role2 have to be names registered by RegisterRole. Alias11, Alias12, Alias21, and Alias22 are names under which the roles can be found too.

      In addition to the configuration store a role maintains a data store. This is the preferred place to store role-specific session state.

      Lookup() looks up an Anything by name first in the role’s data store then in the configuration store. Typically Lookup() is not called directly on a role object but through the lookup mechanism of a Context object (see chapter 2.5).

      Methods IsSecure and IsAdmin can be overridden for roles which need special privileges. If IsSecure returns true switching to this role requires authentication. If IsAdmin is true it requires authentication and gives unrestricted access. The default implementations look up the Boolean value of the two fields "IsSecure" and "IsAdmin" respectively from the role’s configuration file.

       


2.4. Class Page

      A Page represents the generation process of a Web-page, not the page data itself! (the latter is a Reply object).

      A page itself uses Renderers in order to generate the page contents. Because these Renderers can be configured with a page specific configuration file ("pagename.any"), it is rarely necessary to subclass Page. But if it becomes necessary subclasses of pages must be registered in a page registry using the macro RegisterPage(subclassname).

      Page aliases can be defined in the main COAST configuration file (Config.any). The format of the associated Anything is:

      /Pages {
         
      /Page1 { "Alias11" "Alias12" … }
             /Page2 { "Alias21" "Alias22" … }
      }

      Here Page1 and Page2 have to be the subclass-names registered RegisterPage(). Alias11, Alias12, Alias21, and Alias22 are names under which the pages can be referred too. Pages can be looked up by name using the static method Page::FindPage.

      If a page has an alias then the associated configuration file is determined by using the alias name, not the class name. This allows to use a single page subclass with a number of different configuration files.

      In contrast to sessions and roles pages are shared! As a result pages cannot have state associated with them that is used throughout the rendering process. If it is necessary to maintain state during the construction of a page, the TmpStore of the Context can be used.

      Lookup() looks up an Anything by name in the page’s configuration store. If it cannot be found it calls Lookup() on its "super" page. Typically Lookup() is not called directly on a page object but through the lookup mechanism of a Context object (see chapter 2.5).

      The most important methods of Page are Start() and Finish(). Both are called from the Session’s RenderNextPage method. Start first prepares a page and then renders the page into an Reply object. Afterwards the page is displayed in the user’s browser. If he or she clicks on a link contained in the page the link is sent back to COAST and the original page gets a chance to post process the request by means of the Finish() method. These methods shuold not be overidden by subclasses.

      Internally Start calls first PreProcess and then Render. PreProcess can be overridden to initialize or prepare data structures for rendering. Executing a data base query or a host transaction are examples for functionality that goes into PreProcess.

      Render can be overridden to "render" the data structure (typically in HTML format) into an Reply object. Because the default implementation of Render implements the rendering process by means of an interpretative data-driven mechanism, it is rarely necessary to override this method.

      The default implementation of Page::Render provides two ways of flexibility. First it tries to find a renderer /PageLayout in the associated configuration store. If such a renderer is found it is passed to Renderer::Render() for further creation of the page. If no /PageLayout is specified Page uses a default template which calls the hook methods Header(), Title(), Body(), Footer() in sequence, and inserts <hr> HTML-tags after the generated header and before the generated footer. These methods can be overridden in subclasses of Page.

      The Finish method internally calls PostProcess(). You can override PostProcess to do any page specific post processing of a request. The default implementation uses the "action" field contained in the request to look up and execute a so called Action object (see chapter 2.6). If you implement all your post processing code as Action objects, there is no need to override the PostProcess method of class Page.

       


2.5 Class Context

      Objects of class Context provide a means to get read and write access to various states from within shared objects during the page rendering process. A Context object keeps references to the current Server, Session,Role and Page objects. In addition a Context provides temporary storage until it goes out of scope. It also provides access to the query and the environment variables set by the HTTP-Server. It also caches the Language to render a specific request.

      For every request a single context object is created in Session::RenderNextPage and subsequently passed to almost every method called by RenderNextPage. The Context and its associated temporary storage is discarded after the request has been processed.

      The most important method of Context is Lookup(). Lookup’s implementation follows the Chain-of-Responsibility design pattern and walks up the lookup chain until it finds an entry with the given name. The lookup chain consists of the following member data and configuration stores and is searched as listed in sequence:

      Context::fTmpStore
      Role::fRoleStore
      Context::fSession
      Context::fPage
      Role::fConfig
      Server::fConfig

       


2.6. Class Action

Class Action is a convenience class for making the post processing of requests reusable. Instead of subclassing Page and overriding its PostProcess method you can subclass Action and override its single DoAction method. The default implementation of Page::PostProcess tries to find an Action object by name and calls the DoAction method. This method serves two purposes.

  • If overwritten in a subclass it allows to execute code in the context of the page that is about to be left by the user. For example this can be used to check parameters entered via a form, or to call administrative functionality of the Server.
  • The return value of DoAction is taken as a name to look up the next page in the configuration store by Role::GetNewPage(). The Role configuration file section /Map is used to associate these "action names" with the resulting pages.

The default implementation of DoAction does nothing and returns the page index string "Next". Subclasses should return the name they are passed as a first parameter if there is no compelling reason not to.

Subclasses of actions must be registered in an action registry by using the macro RegisterAction(name) in the implementation of the class. Action aliases can be defined in the main COAST configuration file (Config.any). The format of the associated Anything is:

/Actions {
    /Action1 { "Alias11" "Alias12" … }
    /Action2 { "Alias21" "Alias22" … }
}

Action1 and Action2 have to be names registered with RegisterAction. Alias11, Alias12, Alias21, and Alias22 are names under which the actions can be found too. Actions can be looked up by name using the static method FindAction.

The static method ExecAction combines looking up the action object by name and calling its DoAction method.

Objects of class Action are shared! As a result actions cannot have state associated with them. If is becomes necessary to maintain state for an action, it should be stored int the context (TmpStore, RoleStore SessionStore).

 


2.7. Class Reply

      A Reply object represents a generated page. It is produced as the result of the page rendering process.

      A Reply object consist of two parts: a MIME-header and a body. Both parts are implemented as dynamically growing buffers (of type String). Various Append() and AppendHeader() methods can be used to fill both parts.

      PrintOn() first adds a key/value pair specifying the length of the body to the MIME header and then writes both header and body to the specified stream.

       


2.8. Class Renderer

      A Renderer implements a single virtual method RenderAll to write some output (typically HTML code) to an object of type Reply passed in as an argument. Access to session state is via a Context object, configuration information is passed in as an Anything argument.

      Object of class Renderer are shared! As a result renderers cannot have state associated with them. If it becomes necessary to maintain state for a renderer or pass data between Renderers, it should be stored in the Context (TmpStore, RoleStore, SessionStore).

      Subclasses of Renderers must be registered in a renderer registry by using the macro RegisterRenderer(name) in the implementation of the class and passing the renderer’s class name. Renderer aliases can be defined in the main COAST configuration file (Config.any). The format of the associated Anything is:

      /Renderers {
          /Renderer1 { "Alias11" "Alias12" … }
          /Renderer2 { "Alias21" "Alias22" … }
      }

      Renderer1 and Renderer2 have to be names registered by RegisterRenderer. Alias11, Alias12, Alias21, and Alias22 are names under which the renderers can be found too. Renderers can be looked up by name using the static method FindRenderer.

      To use a Renderer clients typically call the static method Renderer::Render(Reply &reply, Context &c, Anything &config). Renderer::Render interprets the Anything config as a Renderer specification, that is a structured Anything containing a Renderer type name (class name or alisa name) and more configuration data.

       

      E.g. if ‘a’ is an Anything with the following structure:

      {
          /Regular {
              /Type String
              /Data {
                  /Default "a String"
                  /D "eine Zeichenkette"
              }
          }

          /Shortcut {
              /Type String
              /Default "a String"
              /D "eine Zeichenkette"
          }
      }

      the following C++ statements are equivalent:

      Renderer::Render(out, context, a["Regular"]); //preferred
      Renderer::Render(out, context, a["Shortcut"]);

      The first variant is the "preferred" format!

      The following sections describe the most important subclasses of Renderers. The name in parenthesis denotes an alias or shortcut. The configuration structure is always shown in the "preferred" format.

       


2.8.1. Class StringRenderer (String)

Renders language specific strings to the reply. The LocalisationUtils class is used for this. The language name defined by context.Language() is used to look up the string to render. Therefore the tags in the data for StringRenderer are arbitrary, depending on the representation of the languages. If "Language" in the Context object is not defined or its value is not identical to any key in the configuration structure the string in slot "Default" will be used.

The configuration structure has the following format:

{
    /Type String
    /Data {
      
  /Default "default string"
        /D "Deutsche Zeichenkette"
        /E "english string"
        /F "..."
        /I "..."
    }
}

         


2.8.2. Class ImageRenderer (Image)

The ImageRenderer renders an HTML <IMG> tag into the reply. If /PathOnly is defined only the image name and the context defined /ImagePath (default = "./") are rendererd to the reply. Otherwise a complete <IMG SRC= tag is created including HTML IMG tag Options given in /Options. Additional valid IMG options for HTML are:

1. SRC="..."--Specifies the URL of the image.

2. DYNSRC="..."--Specifies the URL of a video clip or VRML world. An image can also be specified first using SRC= to cover for browsers that do not support videos. (IE)

3. CONTROLS--Adds a set of controls under the video clip. (IE)

4. LOOP="n"--For video clips, specifies the number of times to loop the clip. A value of "-1" or "INFINITE" makes it loop indefinitely. (IE)

5. START="..."--Specifies when the video clip should start playing. Possible values are "FILEOPEN" (default), "MOUSEOVER", or both.(IE)

6. USEMAP="#map1" --Tells the browser that the image is a client-side clickable image map defined under the name "map1".

7. ISMAP --Tells the browser that the image is a server-side clickable image map.

8. ALT="..."--Specifies a text string to be displayed on browsers that do not support inline images.

9. BORDER="..."--Specifies the width of the border drawn around the image. If BORDER is set to "0", there will be no border even around pictures that are links.

10. LOWSRC="..."--Specifies the URL of an image to be loaded first, before the image specified in SRC is loaded. LOWSRC usually reefers to a smaller image.

11. ALIGN="..."--Specifies the alignment of the image.

Values:

  • 1. RIGHT or LEFT--Aligns the image to the specified side of the page, and all
    text is wrapped around the image.
  • 2. TOP, MIDDLE, BOTTOM, TEXTTOP, ABSMIDDLE, BASELINE, and ABSBOTTOM --Specifies the vertical alignment of the image with other items on the same line.

12. VSPACE="..."--Specifies the space left between the edge of the image and the items above or below it.

13. HSPACE="..."--Specifies the space left between the edge of the image and the items to the left or right of it.

14. WIDTH="..."--Specifies the width of the image. If the width is not the actual width, the image is scaled to fit.

15. HEIGHT="..."--Same as above ,except it specifies the height of the image.

The configuration structure has the following format:

}
    /Type Image
    /Data {
        /ImageName "name of image"
        /PathOnly 1
        /Options "HTML option string"
    }
}

"ImageName" # creates: <IMG SRC="ImagePath/name of image">
"
PathOnly" 1 # just render context.LookUp("ImagePath") and ImageName
"
Options" # may contain an attribute list in HTML-format which is copied literally.

 


2.8.3. Class DateRenderer (Date)

        The DateRenderer is used to insert the current date (and time) into the output stream. Using the tag /Offset it is possible to insert another date relative to the current day. Localization of time values (GMT-offset) is implemented by calling localtime(). The formatting tag /Format depends on the strftime C library function and defaults to %C (= 19 for the current century and 20 for the next).

        The configuration structure has the following format:

        {
            /Type Date
            /Data {
                /Format "format string in Unix strftime format"
                /Offset 0 #offset in days from current time
            }
        }

         


2.8.4. Class ContextLookupRenderer (ContextLookup)

        The ContextLookupRenderer provides indirection of rendering via a renderer specification that resides within the current context. This allows to provide dynamic content via the current context, which in turn might be set by the page's preprocessing or last page's postprocessing action.

        There are two syntactical ways to specify the data of a ContextLookupRenderer. First, by using the explicit tags /ContextLookupName and /Default, second, by just listing two entries in the Anything.

        Note that the default entry can list a complete renderer specification, not just a string to be rendererd (with the new Renderer::Render())!

        {
             /Type ContextLookup
                /Data {
                /ContextLookupName "foo" # name to lookup
                /Default "what to use if ‘foo’ cannot be found"
            }
        }

         

        or just -- this will work after the corresponding Change Request is commited
        {
            /Type ContextLookup
            /Data {
                "name" "default"
            }
        }

         


2.8.5. Class ContextLookupRenderer (Render)

        The ContextLookupRenderer takes the name of a renderer specification as ist input. It looks up this name in the Context and passes the result to Renderer::Render for further processing. The referenced renderer specification can either be a simple value or a string or it is interpreted as a renderer specification if it is an Anything array. Because only a single tag name /RendererName is used this tag name can be omitted for convenience. Then the value of the first entry (it should be only one) is used. If the given renderer name is not found in the context, nothing is rendered.

        Structure of data:

        {
            /Type Render
            /Data {
                /RendererName " name of a renderer in context "
            }
        }

         


2.8.6. Class AppletRenderer (Applet)

        This is a simple renderer used to embed Java Applets into an HTML page. The renderer allows to specify the applet to be used, its basic layout, and the parameters passed to the applet. The renderer expects different settings in the configuration Anything:

        /CodeBase: The location of the java code relative to your web servers document root.

        /Applet: Specifies the complete name of the Java Applet class to be used.

        /Options: contains settings that are directly interpreted by the HTML browser (e.g. the width and height of the applet). Each of these slots my contain either a text literal or a renderer specification.

        /Params: is used to create a variable list of parameters passed to the applet. 'Params' must contain an associative Anything array. Each slot name is directly used to name an individual parameter and each slot may contain either text literal or a renderer specification used to create the respective value of the parameter.

        Example:
        Specification of an AppletRenderer using the Java applet class 'PieApplet' from the package 'CH.ifa.toolkit'. The code base is looked up from the context at slot
        /CodeBase (e.g. 'CodeBase' might be globally defined once in Config.any). The size of the applet is defined to be 350 by 200 pixels. Two parameters /X1 and /X2 are passed to the applet: /X1 is defined to be the text literal '10' whereas /X2 is obtained by looking up 'foo' in the context:

        {
            /Type AppletRenderer
            /Data {
                /CodeBase {
                    /Type ContextLookup
                    /LookupName "CodeBase"
                }
                /Applet "CH.ifa.toolkit.PieApplet.class"
                /Options "WIDTH=350 HEIGHT=200"
                /Params {
                    /X1 "10"
                    /X2 {
                        /Type ContextLookup
                        /LookupName "foo"
                    }
                    ....
                    ....
                }
            }
        }

         


2.8.7. Class TableRenderer (Table) and ItemRenderer (Item)

 

        A TableRenderer inserts a dynamic HTML table into the output stream. The configuration structure has the following format:

        {
            /Type TableRenderer
            /Data {
                /Options "HTML tag argument list for table"
                /Rows "break table into pages with this many of rows"
                /Columns {
                {   # column 1
                        /Title Rendererspecification
                        /Body Rendererspecification
                        /Options "HTML tag argument list for colum1"
                    }
                    { # column 2
                    }
                    { # column n
                    }
                }
                /RowsColors
        {
                    "color for (row % n) == 0"
                    "color for (row % n) == 1"
                    ...
                    "color for (row % n) == n-1"
                }
            }
        }

        The Rows entry is optional. If its missing the table uses all available data. If Rows is smaller than the number of rows in the data, only this many rows are shown and two navigational buttons (Next, Previous) are rendered below the table. TableRenderer generates these buttons by searching a renderer with name "ListNavTable" in the context.

        The other optional entry RowsColors may contain any number of colors in HTML format. The k-th color is used for rows where the row index modulus n is k.

        In order to fill the table with data from a transaction the Body renderer has to be or should contain an ItemRenderer. If the data should be a link the ItemRenderer can be wrapped with a LinkRenderer first.

        An ItemRenderer can only be used within the context of a TableRenderer. Its configuration structure has the following format:

        {
            /Type Item
            /Data {
                /Attr "name of attribute in data"
            }
        }

        If the attribute name starts with a '@'-character it is treated as a special symbol. Right now only "@Index" is understood: this symbol inserts the current row index into the output stream.

         


2.8.8. Class LinkRenderer (Link)

        The LinkRenderer forms a HTTP HREF statement with a Label and an URL (see URLRenderer, URLPrinter, SimpleURLPrinter, FullURLPrinter) The Indirection to the URLRenderer specified in the context is necessary, because different application environments have different requirements on the format of the URLs (with BASE tag, encryption, etc.). Therefore the complete config of LinkRenderer is passed to the called URLRenderer. See URLPrinter for additional data elements besides /Label. The additional data is used to generate the "private" part of the link. The /Action string is added to the resulting URL as an additional value argument with key "action". E.g. If /Action "foo" is in the Link-configuration URLPrinter asks the Role to add data to the Link by calling its CollectLinkState() method. By default this method uses /StateFull from ist config to determine the names of the parameters it adds. The corresponding data is expected in the TmpStore with the same name.

        Structure of data:

        {
            /Type Link
            /Data {
                /Label <Rendererspecification>
                # URLPrinter data:
                /Action "action name will be added to URL"
                /FromContext {
                    /ContextLookupName "lookuppath"
                    /ArgumentName "argname"
                }
                /Parameters {
                   /nameofparam1 "rendererspec. 1st parameter"
                    /nameofparam2 "rendererspec. 2nd parameter"

                }
            }
        }

         


2.8.9. Class HTMLTemplateRenderer (HTML)

        The HTML template renderer is a generic renderer that allows to render arbitrary strings or texts. It defines a specific macro substitution mechanism that is used to embed "calls" to other renderers within the template text to be rendered. The macro syntax is:

        [[#wd renderername rendererdata ]]

        Renderername is a name that is possible as /Type value for which a renderer is registered (or aliased). The rest until the closing ]] is interpreted as an anything specifying the /Data content of the renderer specification. It is typical to use just a ContextLookupRenderer and a name of another renderer specification like [[#wd ContextLookup aRendererspecificationname ]]. Note that the rendererdata MUST NOT contain ]] and that ]] cannot be escaped. If your renderer data will contain ]] somewhere literally you have to use an indirect or you can use the "old style"-macro syntax embedded in a HTML-comment like:

        <!-- # renderername rendererdata -->

        Nevertheless, in this case "-->" is forbidden in rendererdata. The template text itself can be specified in two ways: Either by denoting a file name, which is interpreted in the current context language (specifying the language subdirectory) or giving the text literally as a list of strings. Note that HTMLTemplateRenderer manages a Cache of files/templates that are specified in a separate configuration. These cached templates are also interpreted only once and stored in a preprocessed way in the cache. The cache is built at server-startup time by reading in all files below a given HTML template directory of the server configuration.

        Structure of data:

        {
            /Type HTML
            /Data {
                #only one of the following two specifications is necessary
                /TemplateName "basename of html file; no html extension; no path"
                /Template {
                    "list of string containing HTML"
                    "1st HTML line"
                    ...
                    "nth line"
                }
            }
        }

        If both (/TemplateName and /Template) are present, the string (/Template) takes precedence.

         


2.8.10. Class FormRenderer (Form) and FieldRenderer (Field)

        The FormRenderer is a specialized version of the HTMLTemplateRenderer that is used to create HTML forms. It closely interacts with the renderers for HTML form elements (<INPUT>) that are defined below. The basic working is that the generated HTML part is embedded in a <FORM> </FORM> tag. In addition it provides the current session state as encoded hidden fields "X= " similar to the way the LinkRenderer encodes the state for a single Link.

        The tag /Method defines the way HTTP transfers the arguments back to the server. The tags /Action /LookupAction define what "action" is put into the link generated for form-processing. Either the action is specified literally or it is searched in the context. The HTML template specifying the form content is specified as with the HTMLTemplateRenderer. Note that it should contain at least one <SUBMIT> tag (e.g. using a FieldRenderer), otherwise no activity can be exploited by using the form.

        Structure of args:

        {
            /Type Form
            /Data {
                /Method "GET or POST"
                /Action "actionname"
                /LookupAction "name of actionname in context"
                /TemplateName "basename of html file containing the middle part of a
                               form; no .html extension; no path"
                /Template {
                    "list of string containing HTML"
                    "1st HTML line"
                    ...
                    "nth line"
                }
            }
        }

        If both (/TemplateName and /Template) are present, the string (/Template) takes precedence.

        Fields can either be specified in HTML directly in the template or by using FieldRenderers.

        The FieldRenderer renders all the possible Form input fields. The configuration structure has the following format:

        {
            /Type Field
            /Data {
                /Control "the type of field"
                /Name "name of field"
                /Value "initial value"
            }
        }

        Possible values for /Control are CHECKBOX, HIDDEN, IMAGE, RADIO, RESET, SUBMIT, TEXT. /Name defines the name of the field and will be sent back as the key part of the key/value pair generated by a form. /Value is the initial value of the field and will be sent back as the value part of the key/value pair generated by a form.

         


2.8.11. Class ConditionalRenderer (Condition)

        This renderer looks for the slot "name" in the context and renders the given renderer specification depending on the value of slot "name". The lookup can result in the following values: True, False, Defined, and Undefined. For every value a different renderer can be specified. True and False are used when the value of slot "name" is of type Anything. Otherwise the condition is either interpreted as Defined or Undefined.

        The configuration structure has the following format:

        {
            /Type ConditionalRenderer
            /Data {
                /ContextCondition "name"
                /True "Renderer for Condition result == True"
                /False "Renderer for Condition result == False"
                /Defined "Renderer for Condition result == Defined"
                /Undefined "Renderer for Condition result == Undefined"
            }
        }

        Example:

        /ConditionalText {
            /Type ConditionalRenderer
            /ContextCondition "ShowConditionalString"
            /True {
                /Type String
                /Default "wuffel"
            }
        }

        The string wuffel is rendered only if the slot /ShowConditionalString is defined (somewhere in the Context) and has the boolean value TRUE (i.e. is not 0). As long as the condition is not satisfied, the conditional renderer will not create any output. (The renderer might be activated by defining /ShowConditionalString 1 in Config.any.)

         


2.8.12. Class SwitchRenderer (Switch)

        The SwitchRenderer provides an indirection depending on the value of a context element. The name is searched in the context. Its string value determines which kind of Renderer is activated. The inner workings are like the conditional renderer, except that the condition is evaluated on a string basis and not on a boolean basis. If the name is found, but its string representation is the empty string ("") the special /Case entry /_IsEmpty is used as a renderer specification. The tag name /ContextLookupName can be omitted if the "name" is the first entry in the configuration Anything.

        Note:
        The
        /Default tag is optional but the /Case tag is required. Nothing is done if no default spec is given and the value of the context lookup of name does not match any of the entries in the /Case Anything.

        Structure of data:

        {
            /Type SwitchRenderer
            /Data {
                /ContextLookupName "name"
                /Case {
                    /xxx { Renderer specification for
                            context.Lookup(name) == xxx }
                    /yyy { Renderer specification for
                            context.Lookup(name) == yyy }
                    /_IsEmpty { Renderer specification for
                            context.Lookup(name) is empty }
                }
                /Default "Renderer specification for Default case"
            }
        }


2.8.13. Class URLRenderer (URL)

The URLRenderer renders just the URL (e.g. for placing it in an imagemap etc.). Depending on the over-all configuration it uses either a FullURLPrinter (without BASE tag) or the SimpleURLPrinter when a <BASE> tag is specified for a page.

Structure of data:


{
    /Type URLRenderer
    /Data {
        /Action "action"
        /FromContext {
            /ContextLookupName
"lookuppath"
            /Argname "argname"
        }
        /Parameters {
            /nameofparam1 "rendererspecification for
                           generating first parameter"
            /nameofparam2 "rendererspecification for
                           generating second parameter"

            ....
        }
    }
}

The utility class URLPrinter is used to abstract from the concrete URL Format. URLs are the main link of generated pages back to the current Coast session. Unfortunately some browsers (MSIE) cannot handle URLs of arbitrary length. In addition, pages with many URLs are common, therefore flexible mechanisms are needed to shorten the generated URLs and thus the generated pages. Another issue is, that the syntax of URLs are slightly dependent on the place where they are used within a MIME output. For all these reasons (and some more) the URLPrinter hierarchy was introduced. Specific subclasses are defined to generate exactly the amount of state within the URLs that is required in a given HTML context (see explanations below). The hook-methods to be overwritten by derived classes are:

  • RenderState -- template method called by RenderAll, calls GetState which collects the link state from the context then RenderPublicState and then RenderPrivateState.
  • RenderPublicState -- renders "beginning" of URL depending on some general configuration settings (UseBaseURL, BaseAddress) and the content of the environment passed by the HTTP server; also generates adr= and port= if given in the "linkstate".
  • RenderPrivateState -- renders the "private" and usually encoded part of the state put into the URL. Different link generation contexts require different names of the parameter (X, X1 and X2) these parameter names are known by the COAST gateway program (wdgateway), that decodes them.
  • BuildPrivateState -- sets up the state Anything to be encoded by RenderPrivateState. It uses the data structure above to derive what parts of the context should be put into the state, and what kind of action should be triggered by the generated URL. The /FromContext tag allows to specify a context lookup, the returned string value is inserted as argument with the name given as the value of /ArgumentName. The /Parameters tag allows to generate paramter string values either explicitely or anonymously by arbitrary renderer specifications. Anonymous parameters are named /P# within the state where # is the position within the /Parameters list. The /Parameters mechanism allows to specify the amount of state passed via a specific URL. The Parameters and the data generation mechanism are specified locally.

 


2.8.14. Class TextAreaRenderer (TextArea)

The TextAreaRenderer renders a HTML <TEXTAREA> tag into the reply. /Name defines the fieldname. With /Value an optional default value can be defined. The key /Enabled is used to disable the input into the textarea (optional). The /Options key filled in additional valid TEXTAREA options for HTML (e.g. ROWS=##, COLS=##).

Structure of data:

{
    /Type TextAreaRenderer
    /Data {
        /Name "name of the field"
        /Value "Your comments:"
            #Default Value to be filled (optional)
        /Enabled 0 #Condition to disable input
        /Options "any allowed HTML option"
    }
}


3. Infrastructure Classes

3.1. Class HashTable

      HashTable is a simple hash table implementation. The key is always a const char*, the value is of type IFAObject*. The implementation is based on open chaining and rehashes itself into a bigger table if the table gets too full (>75%).

      Because the lookup is based on an external key, the value object (of type IFAObject) doesn't have to implement neither a Hash() nor an IsEqual() method.

       


3.2. Class Registry

      The class Registry implements a registration mechanism based on Hashtable.

      It is used for registering all instances of key classes like sessions and roles. It is also used to register prototypes or singletons of the application specific subclasses of the key abstractions (Role, Page, Renderer, Action).

      The classes that require registration from their subclasses define a static member registry. Then a XXXXInstaller class is defined in addition with a macro for syntactically easy registration. The following code shows an example of the registry mechanism.

      Class Role {
          …
          static Registry fgRegistry;
      }

      class RoleInstaller {
          public:
          RoleInstaller(const char *name, Role *r);
      };

      #define RegisterRole(name) \
      static RoleInstaller _NAME2_(name,Registerer) (_QUOTE_(name), new name())

      The macro RegisterXXXX(name) is used as in the following statement in the implementation files of derived classes on file scope.

      RegisterRole(Admin); // register prototype instance for class Admin

       


3.3. Class Thread

      Class Thread is a simple wrappers for Solaris threads. The API is loosely based on the Java classes Thread and Runnable. Subclasses of Thread implement a method Run() to be called in a separate oprating system thread. Whenever Run() finishes the thread object is deleted. Therefore, instances of type Thread can only be allocated dynamically. The thread they represent is then started by calling the Thread::Start() method. The classes of the following two sections complete the simple thread package with synchronization mechanisms.

       


3.4. Class Mutex and LockUnlockEntry

      Class Mutex wraps Solaris mutex_xxx functions as an object. Use it directly and call the Lock or Unlock methods or use the LockUnlockEntry convenience class. A LockUnlockEntry object declared in a block will lock the associated mutex on block entry and release it on exit. This is especially useful when the block contains more than one exit (e.g. multiple returns).

      Because a LockUnlockEntry object is just declared and not used, some compilers generate a warning message. Calling the method Use() prevents this.

       


3.5. Class Condition

      Class Condition wraps Solaris cond_xxx functions as an object. Use a Condition object to atomically block threads until a particular condition is true.

      Always use Condition together with a Mutex.

       


3.6. Class String

      Class String is a simple implementation of dynamically growing character buffers.

      When used as a general purpose String class the implementation is not very efficient because lots of copying takes place.

       


3.7. Class Stream

      The stream classes provide just enough API and implementation to support the Anything import/export mechanism.

      With IStringStream and OStringStream Anythings can be streamed into Strings and constructed from Strings. ISocketStream and OsocketStream provides reading and writting from/to a socket.

       


3.8. Class Anything: A Dynamic Data Structure

      An Anything is a polymorphic, self describing, and dynamic data structure. An Anything can represent simple data types like longs, boolean, doubles or strings, arrays of Anythings, and dictionaries mapping between strings and Anythings (i.e. associative arrays). Because Anythings are recursive by definition, they can model arbitrarily complex structures. Since Anythings are dynamically extensible they can adapt at runtime.

      A simple example shows how Anythings can be used as a polymorphic data structure:

      Anything
          any1(123),
          any2,
          any3("abc");

      any2= 3.14;
      any1= any2;

      cout << any3.AsCharPtr() << " " << any2.AsDouble());

      Anythings provide structure beyond simple name and values, for example the following is an array of employee records of which each itself contains an address record:

      Anything employees;

      Anything anEmployee;
      anEmployee["LastName"]= "Smith";
      anEmployee["FirstName"]= "Jane";

      Anything address;

      address["Country"]= "USA";
      address["City"]= "Palo Alto";
      address["Street"]= "123 Main St.";
      address["State"]= "CA";
      address["ZIP"]= 94301;

      anEmployee["Address"]= address;

      employees.Add(anEmployee);
      employees.Add(anotherEmployee);

      Anything are used within COAST whenever dynamic extensibility is useful or necessary. An illustrative example is the session store. Because most objects are shared and all session state is stored in the session object, it would be a royal pain if one had to subclass Session in order to add an additional member. Using a single Anything member provides an easy to use mechanism to add any number of members without subclassing.

       


3.8.1. Import/Export format of Anythings

        Another important aspect of Anythings is the human-readable import/export format. The following is a "dump" (cout << Anything;) of "employees" from the example above:

        { # array with 2 elements
            { # 1st array element; a record
                /LastName "Smith"
                /FirstName "Jane"
                /Address {
                    /Country "USA"
                    /City "Palo Alto"
                    /Street "123 Main St."
                    /State "CA"
                    /Address 94301
                }
            }
            { # 2nd array element
                /LastName ...
                ...
            }
        }

        A comment begins with a '#' and ends at the end of the line. Comments are discarded when an Anything is read from a stream. This format can be streamed into an Anything with a single statement.

         


3.8.2. Anythings as a Configuration Mechanism

        Anythings are an excellent configuration mechanism because the import/export format is easy to read and write and very powerful due to its recursive nature. Adding new structured data types to the configuration mechanism never requires that the syntax has to be extended. Because the syntax is stable there are no syntax related versioning problems: old programs can import new structures without having to understand them.

        On the other hand using Anythings from within a program is simple because there is only little API of a single class to learn.

        Anything are used as a configuration mechanism by most of COASTs classes, e.g. Server, Role, Page, Renderer. Typically every object of these classes has an instance name. This name is used at creation time of the object to locate an Anything configuration file (*.any). If there is no instance name, the class name is used instead. The configuration file is then streamed into a member of type Anything. Because all of these objects are singletons configuration information is loaded into memory only once. Subsequently every method has access to the configuration information.

         


3.8.3.Usage of Anythings in Coast

        Anythings are the configuration mechanism used throughout the Coast framework. The following sections briefly describe how it is used for the specific part.

3.8.3.1. Global configuration

          There is a global configuration file "Config.any" to be read at start-up time. This configuration file defines the default data to be used by the server. It also contains information denoting further configuration stuff. Mandatory entries (besides server configuration) are:

          /Roles

          defines list of available roles (class names)

          /Pages

          defines list of available pages (class names and/or aliases)

          /Renderers

          defines list of available renderers (class names with aliases)

          /Actions

          defines list of available actions (class names with aliases)

           

          Elements used by the Server class (sometimes optionally) are:

          /port

          defines IP port number to be used by Coast server

          /SessionTimeout

          defines global timeout in seconds for sessions


3.8.3.2. Debugging subsystem

The debugging subsystem is completely controlled and fine-tuned with a configuration file called "Tracer.any". This file is used to control the calls to the methods of class Tracer. The names of the sections relate to the names used in the constructor of the Tracer objects. The convention is to use a dot-notation that relates to classname.methodname. This makes it easy to define the debug information according to the program structure.

You can enable and disable debugging statements by editing the file "Tracer.any". The structure of this file is hierarchical and reflects the dot-delimited structure of the trigger strings used as the argument for Tracer. A "Tracer.any" file for the above example would look like the following:

{
    /MainSwitch 10
    /EnableAll 20
    # switches with values between MainSwitch and
    # EnableAll will generate output
    /Bar {
        /MainSwitch 15
        /EnableAll 0
        /Foo 15
    }
}

The first level contains a Anything "Bar" with a subkey "Foo". The value 1 turns on tracing information for trigger "Bar.Foo". This relates to a situation with a class Bar and a member function Foo (which in turn defines a Tracer t("Bar.Foo",…);

On every level there are two override switches:

  • MainSwitch defines the minimum value to enable tracing at this level. If it is set to zero no tracing information for this level and all nested levels takes place. Therefore you need to enable the top-level MainSwitch in "Tracer.any" to generate any debugging output.
  • EnableAll defines the maximum value to enable tracing.

 


3.8.3.3. Pages

          Pages might be defined solely by just defining a specific configuration file for them, without using C++ subclassing. For each page name registered within the main configuration file a corresponding configuration file with the name of the page and extension .any has to be defined. The aliased page ‘inherit’ the configuration information from their "superpages", which in turn must be defined in the config file <superpagename>.any. This configuration inheritance works also for pages aliased to already aliased pages. This is a convenient way to have common definitions in just one place.

          The standard page defines the element /PageLayout. This typically defines an HTML renderer (class HTMLTemplateRenderer) and either points to a corresponding HTML template file or literally defines the HTML template.

          Further entries within the page configuration file can be used to pass parameters to embedded renderers, for example definitions of standard page elements. These definitions are passed to the renderes within the context (class Context) of the rendering process.

           


3.8.3.4. Renderers

          Rendereres do not have individual configuration files. However, they are part of pages and thus individual ‘calls’ to renderers define concrete parameters to be used by the renderer. The renderers are accessed by the following anything structure:

          /Type <renderername>
          /Data { <parameters to the renderer> }

          Please refer to the COAST documentation for details of specific renderer data.

           


3.8.3.5. Roles

The standard implementation of Roles defines the entry /Map to contain a state map which define the page name to be used after a specific action. For each page it lists the reachable page names indexed by an "action" name. A /Default entry in the map is used for conveniently defining subsequent pages valid for all pages.

Internally (and in the configuration files) the decision for the next page is a two step process.

  • The entry /Action of the old Link defines the name of an Action object to be used during post-processing. The name is used to get an Action object from the Registry. The object is sent a DoAction message and the result determines the new name.
  • The result of the action (a name) then determines the entry in the map (under the old page) to be consulted for the name of the new page.

However, if a corresponding action of the first lookup is not defined, the name is used directly as the index into the page map of the Role.

Example:

/Map {
    /Default {
        /Help "HelpPage"
    }
    /Page1 {
        /Next "Page2"
    }
    /Page2 {
        /Next "Page3"
        /Previous "Page1"
    }
    /Page3 {
        /Previous "Page2"
    }
}

If GetNewPage is called for an action "Next" on page "Page1" it will return "Page2". If its called for an action "Next" on page "Page2" it will return "Page3". If it is called with action "Help" for any page, it will return "HelpPage".

You can subclass Role in order to provide a more sophisticated implementation for the state machine in GetNewPage().

 


3.8.3.6. Actions

          The class Action and its subclasses implement the Command pattern. Actions do not have a separate configuration file. However, each action subclass can be aliased within the main configuration file. The (alias) name is passed as aparameter to the DoAction() method.

           


3.8.3.7. Searchable flexible Storage

          These Anything attributes are used for passing arbitrary data structures between different function calls. These might even be calls not in a single call chain (e.g. passing data between different renderers activated in sequence). Several classes within the Coast framework provide such generic storage (Session, Role, Context). This allows to add ‘data members’ to these non-shared objects on behalf of the shared instances of Page and Renderer subclasses. In addition the parameters of a HTTP Request (HTML form fields) are passed in the Context as an Anything named query.

          The config information of the Server instance is accessed in a similar way via the Context object passed along for each individual request. Class Context provides a searchable combination of the configuration inforamation in a prioritized way. It implements the Whole-Part design pattern.

           


3.8.3.8. Parameters

Either Anythings objects are used directly as parameters (sometimes input and output) or the wrapping class Context is used. Using Anythings enables the framework to keep stable interfaces, even if subclasses require more information than originally supported.

 


 

3.9. Class System

      Class System contains all system related convenience functions.

      OpenIStream and OpenOStream provide read and writting from/to a file by looking into different directories. These directories are specified as a colon-separated path list in the configuration file "Config.any" (/PathList). Method Server::StdInit in Main() loads the pathlist from the file "Config.any". If no pathlist is found in config, the default pathlist ".:src:config" is defined. There are different methods for managing the ‚searchpath‘ in the class System.

       


3.10. Class Tracer

Class Tracer implements a configurable tracing and debugging mechanism. StartTrace1 creates a Tracer object in a block or method and pass in a identifying string (a "trigger") and an additional message string. At run time you will see an enter and an exit-message for your block on cerr. The messages will show the name of the trigger and the message string. It is necessary to trace additional statments a function Trace can be used to pass a message string. The function SubTraceAny allows tracing of Anything’s using an additional trigger to increase the output granularity.

Example:

void Bar::Foo(Anything config, int i)
{
    StartTrace1(Bar.Foo, "additional info string");

    Trace("another message in my block");

    SubTraceAny(Config, config, "Config:");

    // ...
    FooBar(i); // now call FooBar
}

You'll get on cerr:

Bar.Foo: --- entering --- additional info string
Bar.Foo: another message in my block
FooBar: --- entering
FooBar: --- leaving
Bar.Foo: --- leaving ---


You can enable and disable debugging statements by creating and editing the "
Tracer.any" file. The structure of this file is hierarchical and reflects the dot-delimited structure of the trigger strings used as the argument for Tracer. A Tracer.any file for the above example would look like the following:

{
    /MainSwitch 10
    /EnableAll 20
    # switches with values between MainSwitch and
    # EnableAll will generate output
    /Bar {
        /MainSwitch 15
        /EnableAll 0

            /Foo 15
    }
}

The first level contains an Anything "Bar" with a subkey "Foo". The value 1 turns on tracing information for trigger "Bar.Foo".