Table of contents 1 Introduction/Overview
1.1
External view/Environment3.8.3.1
Global configuration
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 ]]
Web Browser:
Any standard Web browser. Advanced capabilities, e.g. support for Frames, Java, JavaScript are not required by the architecture of COAST. But if they are available COAST can take advantage of it.HTTP Server :
Any HTTP server with support for calling external programs or communicating with another server, e.g. the Common Gateway Interface (CGI), the Netscape Server API (NSAPI), or the Internet Server API (ISAPI).Gateway:
wdgateway (for COAST Gateway). acts as an intermediary between HTTP servers and COAST. The gateway insulates COAST from HTTP server interfaces by handling all communication aspects. Because most HTTP servers support the Common Gateway Interface (CGI). By default wdgateway is configured as a CGI program, but it is easy to convert wdgateway into a server extension.
wdgateway extracts all necessary information from the CGI calling environment (e.g. URL, query arguments, type of client browser, etc.) into a canonical data structure and streams this structure via a socket to COAST. Any data returned from this socket is passed back to the HTTP server.COAST-Server:
COAST-Server is a multi threaded server process which handles requests coming from wdgateway by returning a stream of data, usually a dynamically generated HTML page. In order to maintain state across requests, COAST associates all requests coming from a single user with a session. The session defines the context in which a web application is executed. Building a web application on top of COAST involves three activities:
- creating various HTML components and templates
- configuring other already existing components by writing configuration files
- extending a framework based architecture by programming.
- Data-Access:
In many cases COAST-Server implements the "presentation" of data stored in a database.
1.2. Internal view of COAST
The figure below shows a high level view of COASTs internal architecture. When processing a single request three phases can be distinguished: on the left hand side requests coming from wdgateway are accepted from the main thread and then immediately dispatched to another thread which processes the request. The first step in processing a request is to determine its associated session object or to create a new session. In the second phase (center of figure) based on the sessions state and the users current role a next page is determined. The third phase is constructing a page from components, so called Renderers. Eventually the resulting page is returned to wdgateway.
[[#wd ContextLookup InternalArch ]]
1.3. Initial request
[[#wd ContextLookup InitialRequest ]]
When a user first accesses a COAST-Server using an initial URL or a stored bookmark a new Session and a corresponding Role object have to be created. This occurs during the first phase if a request cannot be associated with a existing Session object. When a Session is created it generates a default Role.During the second phase the
Role is responsible for determining the page which has to be refound. For an initial request this process is usually trivial. The Role returns a default page.The third phase, which renders the page and returns it, is the same as always.
An initial request is based on the following classes:
Class Name |
Description |
COAST server |
|
a data structure similar to a dictionary |
|
HandleSession |
Thread processing a single request |
manages session state and controls overall request processing |
|
state machines for page transitions |
|
collects header and contents of a page as a response |
The
Run method of HandleSession first reads the request from the socket into a dictionary-like data structure (an Anything, for a detailed discussion of Anythings refer to chapter 3.8). Then the session information is extracted from the request and used to find an already existing session object in a table. If none can be found a new session is created.The session object is the single most important class of COAST. A session object maintains the state for a user session across requests. A session object is terminated either by an explicit user action (e.g. logout) or by a timeout if the session object hasnt been accessed for some time.
Subsequently the request is passed to the
RenderNextPage method of the session object which generates the pages header and contents into a Reply object. The Reply is then streamed to the writing side of the socket connection. If the user had canceled his last request for instance by pressing the stop button of his browser, streaming back the reply will result in an error. In this case HandleSession::Run just discards the reply object. Eventually HandleSession::Run closes the socket and returns, which terminates the associate thread.
1.4. Normal request
Subsequent requests can be associated with an exisiting
Session object during the first phase. The request contains the identifier of the previous page which is then asked to Finish. At the end of the Postprocessing the action object corresponding to the action of the request (if it exists) is sent a DoAction method.If an action object exist, it may rename the action as a return value. This symbolic action is used by the Role to determine the following page in
GetNewPage (/Map in file Role.any).[[#wd ContextLookup NormalRequest ]]
The page is then rendered as usual in the third phase. The render process is explained in chapter 1.5.The important classes are:
Class Name |
Description |
Manages session state and controls overall request processing |
|
Renders a page and handles actions |
|
An action |
|
Controls access to application and configuration data |
|
State machines for page transitions |
Before
Render is called another method Preprocess will be called. This method is typically overridden to prepare or initialize the data structures which are required for the rendering process. For example if a page represents a report of host data the associated transaction will be called from within the Preprocess method.After a page has been rendered it is transferred to the userss browser. If the page contains links clicking on a link will send a new request back to COAST. COAST maps this request back to a session object in order to establish the original context in which the page has been created. Before determining a new page, COAST first gives the old page a chance for processing the request by calling a method
Postprocess on the old page. A typical example of postprocessing would be the validation of a form.One way to create different pages is to derive new classes from
Page and override the methods Render, Preprocess and Postprocess. If your Web application has many pages this is getting cumbersome. To make things easier to use it is possible to implement the postprocessing code as a subclass of a class Action and install it in a registry. The default implementation of Page::Postprocess looks up the action by name and calls the DoAction method on it. This way postprocessing code can be easily reused and overriding Postprocess for every page can be avoided.The most important responsibility of phase 2 is to determine the next page of a request. This functionality is based on the concept of a
Role. A role is an abstraction for all functionality that depends on the role a user plays. The session objects maintains a current role by referencing a role object. Role objects are not shared because they typically maintain role specific state for the session. The session object delegates all role dependent methods to the role object. The role of a session depends on the users capabilities or access rights and is typically derived from the users account information. Usually a new user is given guest-status.The most important functionality of Role is to determine the name of the next page based on a current page and the symbolic name of the current request (action). This functionality has to be implemented the method
Role::GetNewPage. The default implementation uses a table driven algorithm. The table specifies for every page the list of possible actions and the resulting next pages. In order to limit the size of a role table it is possible to specify default transitions which are valid for every page and specify destination pages for unspecified Page/Action combinations.Roles can be implemented either by providing different transition tables for the default implementation of class
Role or to by deriving new subclasses of Role.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 users 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 applications 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:
{
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:
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 sessions constructor or by overriding the (polymorphic)
Init method. Dont 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 users 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:
Role object (see chapter 2.3).
- If the state is closely associated to a role it should be kept in the store of the
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 sessions 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 sessions role remains unchanged.GetNewPage method. The old page (if any) and the current action are passed as arguments.
- Use role to determine next page:
The current role is used to determine the next page by calling its
- 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
The default implementation of
GetNewPage() uses a simple table-driven state machine which is initialized from the roles 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"
}
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 roles 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 roles 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 roles configuration file.
2.4. Class Page
Lookup()
looks up an Anything by name in the pages 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 Sessions RenderNextPage method. Start first prepares a page and then renders the page into an Reply object. Afterwards the page is displayed in the users 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
The most important method of
Context is Lookup(). Lookups 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.Role::GetNewPage(). The Role configuration file section /Map is used to associate these "action names" with the resulting pages.
- 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
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
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.Class Renderer
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 renderers class name. Renderer aliases can be defined in the main COAST configuration file (Config.any). The format of the associated Anything is: /Renderers {
E.g. if a is an
Anything with the following structure: {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)
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.Renders language specific strings to the reply. The
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
}
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())! {or just -- this will work after the corresponding Change Request is commited
Structure of data:
{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
{
/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"
}
....
....
}
}
}
A TableRenderer inserts a dynamic HTML table into the output stream. The configuration structure has the following format:
{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: {
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:
{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:
{
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:
{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: {
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:
{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:
{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.
TextAreaRenderer (TextArea)2.8.14. Class
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=##).The
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"
}
HashTable3.1. Class
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 {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
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
Another important aspect of
3.8.2. Anythings as a Configuration Mechanism
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
Please refer to the COAST documentation for details of specific renderer data.
Roles
/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 standard implementation of Roles defines the entry
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: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".If
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
3.8.3.7. Searchable flexible Storage
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.
System3.9. Class
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 Anythings 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 ---
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:
You can enable and disable debugging statements by creating and editing the "{
/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".