Technical Background

Contents

Logical Architecture

SPL Services

SPL Service XML Metainfo Files

Server Architecture Overview

Client Architecture Overview

SPL Client API

Logical Architecture

 

Logical Architecture of the SPL System

Information is presented in a web browser (Internet Explorer 6.0) using HTML and JavaScript (not Java, i.e. no applets).  The browser communicates with a Web Application Server via HTTP.  The Web Application Server is divided into several logical tiers: presentation services, business logic, and data access.  Inbound HTTP requests are handled by Java Servlets in the presentation layer, which may in turn invoke data service objects.  In turns, these objects may route control to Java-based business entities, which use the Hibernate ORM framework for data access and persistence.  Various static data (control tables for drop-downs, language-specific messages/labels, etc.) are cached in the presentation layer of the Web Application Server.  The presentation layer makes use of XSL/T technology to create HTML for the browser.

As the browser may need several “pages” to show all the information relating to a particular business entity, a JavaScript “model” is used to manage the data in toto, and the Internet Explorer XMLHTTP object is used to send the data to the server as an XML document.  Data is provided to the browser as literal JavaScript.  The specialized portal and dashboard areas use server-side XSL/T technology to render the final HTML directly on the server.  The HTML for grids in the browser is created using client-side MSXML XSL/T transforms.

This kind of architecture has been described by the term “AJAX”, meaning Asynchronous Javascript and XML.

Key Advantages

Contents

Portability

Distribution

Portability

The system is highly portable to various hardware platforms, as web application servers are pure Java applications and run on myriad operating systems, including Windows clients, servers, and many versions of UNIX.

In principle, any compliant Java 2 Enterprise Edition container can host the application.

Distribution

The various logical components can be distributed to as many machines as desired.  In particular, the web application server architecture is stateless, so many parallel server machines can be utilized given an appropriate load-balancing architecture.

SPL Services

The SPL system makes heavy use of “services”, which are data access and update services ultimately implemented in Java, accessing Oracle or DB/2.  Each service invocation represents a distinct database transaction.

There are three kinds of services: Page, List, and Search.

A Page service defines all the data needed to display data on a single tab menu (e.g. across all child tab pages).  The data structure is logically a tree, with a root object containing attributes as field/value pairs, and recursively containing lists of similarly structured objects.  The typical maximum nesting depth is 4 levels of contained lists.  Page service names end with the letter “P”, e.g. CILCACCP.  Page services may be called in five primary “modes”: Read, Change, Add, Copy, Delete, and Default.

List services define a list of objects, possibly containing nested lists.  In addition to being accessible independently for list-oriented data, they can be used to flesh out lists contained in page services where more data is available than can fit in the (fixed-size) buffer.  List services do not support database updates.

Search services are used to support ad-hoc user searches for data.  The results are structurally similar to List services.  The input is a set of criteria and a search mode, with values “MN”, “AL”, “A2”, A3”, etc.

Service requests can return a normal result, or create an error or warning.  A warning displays a message with a list of warning lines, and offers the choice of proceeding (which triggers the same call with a flag set to suppress warnings), or cancel.  Errors create descriptive messages.  Search and List services can only create errors, not warnings, while all Page services except Copy can create warnings and errors.

SPL Service XML Metainfo Files

The SPL system represents the structure of a service using an XML document (loosely akin to an XML schema).  Every service is defined with a single XML document, which is generated based on the Java class information.

Service XML documents are created by using annotation-based metainformation combined with system metadata (stored in the database).

Contents

Example using Page Service

Example Using Search Service

Example using Page Service

The following excerpt of the CIPBSTMP.xml file will motivate the discussion.

<?xml version="1.0"?>

<!-- Service CIPBSTMP -->

 

<page service="CILBSTMP">

 

The root element of the document has the tag “page” to reflect that this is a page service, and describes the service name as an attribute.

   <pageHeader>

      <string name="STM_ID" size="12"/>

   </pageHeader>

 

The <page> element contains exactly one <pageHeader> and one <pageBody>.  The <pageHeader> contains any number of “singleton” fields.  This one is a string field named “STM_ID” in the browser, and with the same name in the original Java source. The field contains up to 12 characters (this is the “business rule” length, not physical storage).

Other types of singleton fields include <bigInteger>, <bigDecimal>, <money>, <date>, <time>, <dateTime>, and <boolean>.

The number-related fields (<bigInteger>, <bigDecimal>, and <money>) have a “precision” attribute, which describes the maximum number of digits that can be represented.  Further, <bigDecimal> and <money> include the “scale” attribute, describing the number of decimal digits appearing after the decimal point.  Thus, an element like this <bigDecimal precision=”4” scale=”2”/> can represent numbers in the range +/‑99.99.

Continuing with the page service example, we have this section:

<pageBody>

  <row actionFlag="ROW_ACTION_FLG">

     <string name="BATCH_CD"

             size="8"/>

     <bigInteger name="BATCH_NBR"

                 precision="10"/>

 

The <pageBody> element contains <row> elements, singleton fields, and <list> elements, in any order.  Here we have another <string> field, as well as a <bigInteger> (an integer value with no decimal fraction), this one holding (up to) 10 digits.  This means numbers in the range +/‑9,999,999,999 can be represented.

The <row> element reflect the java entity (row).  In terms of the infoset and browser the fields in the <row> element are effectively merged into the containing <pageBody>, along with fields from any sibling <row> elements.

The “actionFlag” attribute names the field that contains a flag that determines the server action that should occur against the row.

     <string name="STM_CNST_ID"

             size="10"/>

     <date name="STM_DT" />

     <string name="STM_ID"

             size="12"

             isPK="true"/>

     <string name="STM_STAT_FLG"

             size="2"/>

     <bigInteger name="VERSION"

              precision="5"/>

  </row>

 

The “isPK” attribute marks fields that are part of the logical prime key of the “main” object/table for this page service.

We then see a <list> element:

      <list name="STM_DTL" size="30" service="CILBSTDL" userGetMore="false">

 

The <list> element describes an elaborately structured array of objects.  The element contains exactly one <listHeader> and <listBody>.  Every list within a service buffer has a unique name attribute.  The number of possible list body objects is given by the “size” attribute.  In the event a list service exists independently for the list, it is named by the “service” attribute.  Finally the “userGetMore” attribute switches the system into one of two modes:

<listHeader lastIndex="STM_DTL_COLL_CNT"

            actionFlag="LIST_ACTION_FLG"

            moreRows="MORE_ROWS_SW"

            alertRowIndex="ALERT_ROW">

     <string name="STM_ID"

        size="12"/>

     <string name="LAST_STM_DTL_ID"

        size="12"/>

</listHeader>

 

The <listHeader> element has a “lastIndex” attribute giving the field name that holds the number of elements actually returned, an “actionFlag” describing the operation to be performed on the list (e.g. change, delete), the “moreRows” attribute naming the field that holds the boolean that indicates whether more data remains un-retrieved in the database for the current list, and the “alertRowIndex” attribute, naming the field that holds an index into the list to describe the location of a validation error, used to select the correct item in a browser when presenting the error to the user.

In addition, a <listHeader> can contain any number of singleton fields.  These are typically keys describing how to access this list, and logical “cursor” fields describing how to continue fetching more items. 

  <listBody>

    <row actionFlag="ROW_ACTION_FLG2">

      <bigInteger name="VERSION"

                  precision="5"/>

      <string name="STM_DTL_ID"

              size="12"

              isPK="true"/>

      <string name="STM_CNST_DTL_ID"

              size="10"/>

      <string name="STM_ID"

              size="12"/>

    </row>

    <string name="STM_CNST_DTL_DESCR"

            size="50"/>

  </listBody>

</list>

 

This finishes the <list> element.  Some more singleton elements appear before finishing the <pageBody> and <page>:

 

      <string name="STM_CNST_DESCR"

              size="50"/>

      <boolean name="ACTION_GENERATE_SW" />

   </pageBody>

</page>

 

Example Using Search Service

List service XML files essentially contain a <list> element as the document root, and will not be described further.  Search service XML files are very similar to those for page services.  Here is an example illustrating the differences:

<?xml version="1.0"?>

<!-- XML Java/Tuxedo mapping CIPCACCS

  Automatically generated by makeXMLMap Sat Nov 10 09:08:30 2001

  Source copybooks: CICCACCS CICCACCH -->

<search name="ACCT"

        service="CILCACCS"

        size="300">

   <searchHeader lastIndex="ACCT_COLL_CNT"

                 actionFlag="SRCH_ACTION_FLG"

                 searchByFlag="SEARCH_BY_FLG">

 

The <search> element is the root of the document, and contains a <searchHeader> and <listBody>.  The <search> element is similar to the <list> element described above, and includes name, service, and size attributes.  The <searchHeader> includes the “lastIndex” attribute, which gives the name of the field holding the number of returned elements, “actionFlag” which names the field containing the search action flag, and “searchByFlag” which names the field holding the search “mode”.  The <searchHeader> further contains singleton fields describing search criteria.  These are adorned with extra attributes describing whether they are distinguished criteria that should always be populated from the search client (optional, defaults to “false”), and a criteria group designation.

 

      <string name="ACCT_ID"

              size="10"

              isCriteriaExtract="true"

              criteriaGroup="MN"/>

      <string name="ENTITY_NAME"

              size="50"

              criteriaGroup="AL"/>

   </searchHeader>

 

The <listBody> was described previously, and describes the structure of the elements matching the search criteria.  In addition, the “isReturn” attribute describes fields that should be returned as the result data when a particular result row is selected (optional, defaults to “false”).

   <listBody>

      <row actionFlag="ROW_ACTION_FLG">

         <string name="ACCT_ID"

                 size="10"

                 isReturn="true"/>

         <string name="ENTITY_NAME"

                 size="50"/>

         <string name="ACCT_REL_DESCR"

                 size="50"/>

         <string name="NAME_TYPE_FLG"

                 size="4"/>

      </row>

   </listBody>

</search>

Server Architecture Overview

The Java server is logically divided into several distinct layers, with different responsibilities.  Form the point of view of a request from the browser, there are a handful of general-purpose data-centric servlets that can handle any service requests.  These servlets handle HTTP requests and transform them into data objects and commands for further processing.  Once an appropriate service is identified for handling a request, its metainfo is used to build a rich Java data structure from the (string-based) browser data representation.  This data, in turn, forms the input to a Java service class.

In our terminology, a page service is a self-contained piece of server functionality that principally acts upon a particular “root” Java entity object (and table) and its child entities (tables).  The framework automates the process of mapping data to and from these entities by making use of the service XML metainfo.  These Java objects are also known as domain objects.  These objects are made persistent via Hibernate, which offers the simpler HQL query language as an alternative to “raw” SQL.  In the simplest cases, no human-written imperative code need be written to implement a page service.

For updates, validation is handled via both automatic logic determined by database metadata, including application-level referential integrity checking, and hand-coded validations implemented by change-handler classes.

For presentation-level requests (e.g. HTML constructs) the server uses XSL/T to create HTML from our UI metadata structure.  Since UI layouts are fairly static the web presentation layer uses caching to help optimize performance.

The development process is geared to keeping generated and human-maintained artifacts completely separate.  For instance, we generate superclasses that contain generated code that is e.g. required by Hibernate or the service framework, and programmers will implement subclasses that implement methods for unusual or extra behavior.

The artifact generation is driven principally by parsing special markup in Java sources known as annotations, combined with metadata held in the database (principally relating to tables, fields, and constraints).  On a modern machine (eg. 3 GHz P-4) artifact generation for the entire system takes only a few minutes.

Client Architecture Overview

Contents

Introduction

Client Architecture Discussion

Introduction

The SPL browser client uses many novel mechanisms in order to support the system design goals of high system performance, including low latency and high throughput.  The core design principle is that the system is stateless, meaning only the browser client itself is aware of the session state, that is the application’s context--what data is being viewed/modified and all other information related to a user more typically associated with session state on the server.  This document discusses the important design points that implement a stateless architecture.

Client Architecture Discussion

The web browser client communicates with the Web Application Server via HTTP, and receives two kinds of dynamic content:

The views are served as HTML that contain labels and HTML <SELECT> elements (drop-downs) that are localized to the user’s language.  Since drop-downs are fairly static, the view objects are cached on the browser via the HTTP 1.1 Cache-Control directive in order to avoid repeatedly accessing the Web Application Server for the same content.

The web server creates HTML using XSL/T technology.  The technique is as follows: the original metadata that defines HTML documents is copied from the database into XML files residing in the server.  These files are shipped as part of a SPL system deployment.  At runtime the server converts these UI metadata documents into HTML in two steps.  First, the logical structure is converted into a nearly-final HTML structure, lacking only language-specific information (labels and <select> lists), via XSL/T, using one of a handful of standard XSL/T template files.  The result object is then transformed again, in order to inject language-specfic elements, creating the final HTML.

As a special case, for performance reasons the HTML for grids (lists and search windows) is created using client-side XSL/T (MSXML).

Data is provided via servlets, of which there are only a few.  The data is represented as literal JavaScript, which happens to be very convenient to handle by the browser (since it includes a native JavaScript parser).  The model takes the form of a “tree” of JavaScript object nodes and values, with a distinguished root node.  The model is converted into an XML document string when submitted to the server, using the HTTP POST method.  This is convenient for the Web Application Server because it is equipped with powerful Java-based XML parsers.

Here is a table of servlets, and a brief description of each:

Servlet Name

HTTP Method

Usage

loginInfo

GET

Provides useful global data at login, retained for life of session, such as a map of all system URLs and a definition of menu structures.

pageRead

GET

Returns a data model object given one or more key/value pairs.

pageChange

POST

Accepts a data model representing modifications that should take place against an existing database entity, returns modified model.

pageAdd

POST

Accepts a data model representing a new entity that should be inserted into the database, returns new model.

pageDefault

POST

Accepts a data model describing a triggered “default” operation, returns a model object containing default values.

pageDelete

POST

Accepts a data model representing a database entity to be deleted, returns nothing.

pageCopy

POST

Accepts a data model representing a model that should be duplicated, returns the duplicate.

listRead

GET

Accepts key/value pairs describing a list of database entities, returns said list.

Search

GET

Accepts key/value pairs containing search criteria, returns list of objects satisfying said criteria.

StringSort

POST

Allows locale-sensitive sorting of strings.  Used for sorting strings in grids when user clicks on the column header.

 

It is important to remember that the servlets deal with “pure” model data, and have no visible representation.  Since actual business logic and database access resides in the app server, the servlets take the role of dispatchers and most servlets accept a “service” parameter describing which app server back-end service to invoke.

The model data is combined with the view on the browser client whenever the model changes or the view needs to be refreshed.  This is done by a name-matching scheme where every HTML element that shows a model value has a name that “picks out” a corresponding value from the current model.  All such HTML fields must include the string “data” in their HTML class.

The simplest case is showing a value from the “root” object, in which case the field name, also referred to as the “JS name” simply matches the model’s attribute name.

There is more complexity in the case of lists—every list in the model has a unique name, regardless of nesting depth, so a JS name that combines the list name with the property name suffices to uniquely identify a section of the model.

There are two sub cases of displaying properties of lists.  The first is where the desired index into the list is known (e.g. grids).  In this case, the JS name combines the list name, index, and property name as follows: <LIST_NAME>:POSITION$ELEMENT_PROPERTY, e.g. ACCTS:3$ACCT_ID.  Note indexes are always 0-based in the browser (in accordance with JavaScript arrays).  This example refers to the fourth element of the ACCTS list, and retrieves the ACCT_ID.

The other case is where the desired index is inferred as the “current” index (i.e. scrolls).  Every list in the model keeps track of its current position index, which is used when no external index is provided.  Hence the JS name ACCTS$ACCT_ID refers to the ACCT_ID property of the currently selected/visible element (presumably in a scroll) of the ACCTS list.

In the rare case that header fields should be displayed, they can be accessed using the JS name pattern LIST_NAME#HEADER_PROPERTY.

Generally, whenever a value is changed in an HTML element or focus is moved after making a change, the system attempts to “commit” the change back to the model.  This involves several steps:

  1. Validating the input for syntax, possibly according to the current locale (e.g. dates)
  2. Identifying the relevant model node to receive the change
  3. Updating the node
  4. Marking the node dirty

The last step is important because we wish to know whether the user has made any changes to the model.

Saving changes to the server generally involves the following steps:

  1. Identifying the servlet to be invoked (e.g. pageChange vs. pageAdd) depending on whether the model represents a new or already persistent entity.
  2. Converting the model tree into a literal XML string representation.
  3. Submitting the XML string to the Web Application Server via the appropriate servlet using the POST method, including a parameter describing the service.  This is accomplished via the Internet Explorer XMLHTTP ActiveX object, using a synchronous calling mechanism.
  4. The servlet, constituting a core piece of the Web Application Server “web presentation” layer, retrieves the service parameter, obtains the XML-based metainfo describing the service, and converts the XML request string into a Java object tree, ready to be passed to the “data service” layer.
  5. The data service layer converts the Java object tree into a Jolt data buffer.
  6. The relevant app service is invoked passing in the data buffer.
  7. The result buffer is converted into a Java object tree.
  8. The Java object tree is converted into a literal JavaScript representation (as needed) and transmitted to the browser.
  9. The returned data (literal JavaScript) is mapped into a new live JavaScript object model.
  10. The user interface is refreshed with the new data.

The portal screens make use of XML and XSL/T technology to create HTML.  The dashboard relies on a current application “global context” to drive it.  Each distinct zone within the portal is created by a Java object known as a zone handler.  The handler is responsible for acquiring data and rendering the displayed HTML.  Handlers implement the interface com.splwg.base.web.portal.IportalZoneHandler.  The usual handler is com.splwg.base.web.portal.XSLZoneHandler.  This handler requires two parameters, a page service and an XSL stylesheet.  It executes the page service in read mode, converts the resulting Java data structure into an XML document, and executes an XSL/T transform with the stylesheet to create the final HTML.  The portal framework takes care of common features such as zone expand/collapse.  XML stylesheets are kept in DefaultWebApp/WEB-INF/xsl, and the include.xsl file acts as a common library of XSL templates to provide standard behavior.

User portal preferences are obtained by the server at login and stored on the browser as XML documents represented as JavaScript strings.  These preferences are sent to the web server when displaying a portal.

SPL Client API

Contents

Overview

Client API Discussion

Overview

The SPL system offers a large number of useful JavaScript functions in the client (browser).  These allow manipulation of widgets, data, and triggering requests to the Web Application Server to view another page and/or object.

This document discusses functions that developers of user exit functions may wish to use.

Client API Discussion

Contents

JavaScript Invocation Context

Data Representation and Localization

Core JavaScript Classes

Free Functions

JavaScript Invocation Context

To use client-side JavaScript functions effectively you have to understand the way JavaScript partitions the client window into independent object spaces (via iframes), and the way common JavaScript framework code is made available to those spaces.

While this may be old hat for experienced browser-side JavaScript programmers, it is important to remember that every window or iframe contains an independent object space, with a separate space of global variables and objects.  Adding, modifying, or deleting objects (or classes) in one iframe has no effect on any objects in other iframes.  However, it is possible to refer to objects that “live” in a different iframe with variables in a different iframe.  This is somewhat dangerous; if the iframe that instantiated the object (where its prototype lives) goes away (by being closed, or having its href modified) the object may no longer be able to carry out any operations.  However, “value” objects such as strings or numbers may safely be shared across frames, even if the creating frame closes.

An iframe can have JavaScript code defined directly into its HTML page, or it can include JavaScript code that exists in a separate file.  The latter technique allows the creation of standard “library” code that is available to many iframes, without the network or development overhead of copying the same functions into hundreds/thousands of files.

The subtle point to remember in the foregoing is that even though the same JavaScript file may be included in different iframes, each definition is completely independent of every other.  If the included JavaScript defines a global, then every iframe gets its own separate global variable binding.

Since there are some commonly used objects, most iframes define and initialize global variables to reference these objects.  The “main” object usually refers to the main iframe, containing cisMain.jsp.  This iframe is never reloaded during a client session, and holds the central model object.  The model itself is usually available as the “model” global in most iframes.

Data Representation and Localization

The browser handles localization.  The client browser object model is logically divided into a display (“view”) and data (“model”) layer.  The responsibility for localizing the data values to the user’s locale rests with the display layer, not the model layer.  All code that retrieves model values and prepares them for display makes use of formatting code, for instance to display dates in the user’s preferred fashion.  Similarly, all user input is parsed in the context of the current locale to convert the data into the internal format that is stored in the model (and passed to Servlete Container/Java/database).

The internal format for model properties is to use strings (not JavaScript numbers or dates) for everything except boolean values.

The functions that are responsible for converting model data values into localized displayable ones are named convertInternalXYZToLocal(internalValue), while the reverse conversions are named convertLocalXYZToInternal(localValue).  Note that latter can fail and include extensive validation, possibly triggering error message alerts.

Core JavaScript Classes

These classes are defined in cis.js, which is included by the main frame.

Contents

CisModel

DataElement

List

CisModel

The CisModel class plays two roles.  The first role is to provide the metadata that describes the currently loaded model instance.  The methods that serve this role are static methods, i.e., defined directly on the CisModel prototype object.  These methods are accessible using the syntax CisModel.function(params), assuming you are in the main frame.  If not, use main.CisModel.function(params).  Instance methods and variables are, of course, accessible through any instance of CisModel, e.g. model.pageData or model.getValue(‘ACCT_ID’).

Contents

Data representation

Navigation

CisModel Instance Variables

Static methods

CisModel Instance Methods

Data representation

The data stored in instances of CisModel uses an internal system representation, not a localized representation.  This means code that manipulates the model is unaware of the user’s locale and display preferences.  For instance, date values are always stored using the ISO 8601 string representation YYYY-MM-DD, and numbers are always stored as strings, not JavaScript numbers (because the required precision may exceed that of JavaScript’s native number type).

The data takes the form of a tree of data nodes of two classes, DataElement (the singleton node class holding data attributes as JavaScript properties), and List, which manages an array of DataElement instances.  Every list has a unique name property, regardless of its position in the tree (i.e. independent of nesting depth), making it possible to uniquely identify and retrieve any list by its name.  The DataElement instances each keep a “dirty” flag to mark whether the user modified any properties, representing work that needs to be persisted to the database.

Every element instance is always in one of three logical states:

·         New/Clean (i.e. a “phantom”)

Phantom elements are used to populate otherwise empty lists, to give a starting point in which to enter data.  Unlike other new objects, phantoms are initially clean so they don’t participate in persistence operations until explicitly modified.

Many CisModel methods act as starting points for recursive implementation through the data tree, and methods with the same or similar names are available on the DataElement class.

Navigation

Many methods accept list names as arguments in order to operate a unique list instance within the model data tree.  Since it is possible for lists to be nested inside other lists, the system assumes the intended list is the one identified by the “current” list positions in the ancestor branch.

CisModel Instance Variables

·         pageData

Used to access the root data node (e.g. model.pageData), an instance of the DataElement class.

Static methods

·         parseNames(fieldName)

This function accepts a string containing a JS field name (the id of an HTML element) and cracks it into its constituent components—the list name, index, and property.  The result is an object with three attributes, property and listName (which are null if missing), and position, which is actually a function that takes the current list position as an integer argument.  The reason for this is to allow the calculation of the current position (no index) and a fixed index.  If the list fragment is missing the listName and position are both null.

CisModel Instance Methods

The most commonly accessed instance of CisModel is the central model containing the data for the current page, typically available through the “main” global variable in most frames.  (Recall again that all such globals refer to the same object; hence changes made with code running in one iframe are visible from any other iframe.)

·         getValue(fieldName)

This accessor method returns the data value corresponding to the provided fieldName.  The field name string will be cracked into constituent pieces using the static parseNames() method, in order to identify the instance of DataElement within the data “tree” that contains the desired property, and then to retrieve the property.

·         getOriginalValue(fieldName)

If a model property has been modified, but the change has not yet been committed (i.e. with the Save button), the system tracks the originally retrieved value of the property.  This accessor method can be used to retrieve the original value.  Useful for implementing certain business rules having to do with logical state transitions.

·         canSetValue(fieldName, value)

This method answers a boolean indicating whether the model is capable of accepting the given value for the provided fieldName.  The method would answer false if buffer capacity limits would be exceeded were the change to be accepted.

·         setValue(fieldName, value)

This setter modifies the property identified by fieldName to hold the given value.  Defaulting will not be triggered.

·         setLocalValueWithDefault(fieldName, localValue)

Sets the property identified by the given fieldName to the specified localValue.  This method does not handle conversion errors, so the provided localValue should have already passed syntactic validation.

·         setValueWithDefault(clientWindow, fieldName, value, element, afterFieldUpdateFunction, continuation, forceDefault, skipDefault, successFunction)

Sets the property identified by the given fieldName to the specified value.  This method may trigger a default, and therefore requires further parameters:

Note this function is not ”thread-safe”, in the sense that it cannot be safely called e.g. in a loop that may issue several calls to this function.  The workaround is to make use of the continuation function to “schedule” the follow-up operation.

·         setListPosition(listName, newPosition)

Sets the list identified by the given listName to the position (a zero-based integer value) given by newPosition.  Useful when you want to display a particular element in a scroll.

·         getList(listName)

Answers the list object (instance of List) with the given name.

·         replaceWithNewList(listName, sourceModel)

Typically called from the default handler callback, this method replaces the entire contents of the given list in the receiver (i.e. the model whose method is being called) with the list as provided in sourceModel.  All elements in the list are considered new, and are eligible to be added to the database when the Save button is used.

·         hasRealElements(listName)

Answer a boolean indicating whether the indicated list contains any persistent or dirty elements (not merely a single phantom).

·         getElement(fieldName)

Returns the data element (instance of DataElement) corresponding to the given field name.  The method resolves the list name and position index, if any, in order to navigate to the appropriate data element.

·         markAsNew()

Mark the model “clean” by recursively clearing all dirty flags throughout the tree structure.  Note this is a very “sensitive” method and should only rarely be needed.

DataElement

Instances of DataElement play the role of the nodes in the data model tree.  They have properties corresponding to business attributes, and also define the tree structure by holding references to their parent data element and list(s) of children elements.  The distinguished root DataElement instance in the core model instance is accessed using with the “pageData” property.

Contents

DataElement Instance Variables

DataElement Instance Methods

DataElement Instance Variables

·         _isDirty

This boolean flag indicates whether the business attributes (stored as JavaScript object properties) have been modified since the DataElement was created (either using persistent database attributes or as a new object that will be added later).  This attribute should not be modified directly.

·         _originalIndex

An integer representing the position of this DataElement in its containing list when it was first retrieved from the server.  For non-persistent data elements this value is –1.

DataElement Instance Methods

·         set(property, value)

Set the given property to the given value.  This apparently simple method can trigger a variety of side effects, including intelligently converting key attributes to uppercase (which is the default unless turned off with the tabMenu.shouldNotAutoUppercase property).  The method returns a boolean indicating whether the setter succeeded; reasons for failure include if the existing value is identical to the current value (this is needed to avoid needlessly marking the data element dirty), and if the mutation capacity would be exceeded.

Further, if the modified attribute represents a key value, the change is propagated recursively through all child data elements by matching the attribute names.

In addition, the original, unmodified, property value is retained for future reference, and can be accessed using the originalValue function.

·         originalValue(property)

Returns the original, unmodified value for the given property name.  This is useful for user exit code implementing business rules that depend not only on the new attribute value but also the original value.

·         isNew()

Answers a boolean indicating whether this DataElement instance is new, i.e. created by the user.  Persistent instances answer false.  The method uses the _originalIndex attribute to decide.

·         isPersistent()

The logical opposite of isNew(); answers true only for persistent data elements.  Useful for avoiding excessive use of logical not (!) operators, thus clarifying the intentions of the code.

·         isDirty()

Answers a boolean indicating whether the message receiver data element or any descendant child data element is marked with the _isDirty flag.

·         list(listName)

Answers the list (instance of List) corresponding to the given list name.

·         clearDirtyFlag()

Set the receiver’s _isDirty flag to false.  Potentially dangerous because it subverts the systems automatic dirty tracking system.

·         canBecomeDirty(property, value)

Answers a boolean indicating whether the receiver can accept the given value for the given property.  The method answers false for various reasons, including if the proposed value matches the previous vaule, the receiver is not dirty and its parent list may not become dirty, and the property is not known to the metadata.

List

Instances of List represent a collection of DataElement instances, held by a parent DataElement.  Every List is held by a DataElement, but the pageData “root” node has no parent list (it’s held directly by the model).

Contents

List Instance Variables

List Instance Methods

List Instance Variables

·         parentElement

The instance of DataElement that contains this list.

·         name

The name of the list.

·         position

The integer index (0-based) of the “current” element.

·         header

The JavaScript object representing the list header.  This is rarely accessed.

·         elements

A JavaScript array containing the current list of elements (instances of DataElement).

List Instance Methods

·         size()

Answers the number of elements held by the receiver.  Does not include elements scheduled for deletion.

·         currentElement()

Answer the DataElement instance from the elements collection referred to by the currentPosition instance variable.

·         isDirty()

Answer a boolean indicating whether any element is dirty.  The test is recursive, and answers true if any descendant has the _isDirty flag set.

·         markElementsAsNew()

This convenience method marks all elements in the collection as new, setting the _isDirty flag to true and setting the originalIndex to –1 for every DataElement.  The method acts recursively on all descendant lists and elements.

·         realSize()

Answers the number of elements, disregarding any phantom element.

·         hasRealElements()

Answers true if the list contains at least one non-phantom element.

Free Functions

 

Contents

top.js

cis.js

top.js

This is the set of “free” functions available in top.js, which is included by cis.jsp.  You typically access these functions using the top.xyz() syntax, assuming your code is running in an iframe nested under cis.jsp.

The trend has been to de-emphasize the use of functions at this level, and migrate them to the main level.  In the future we plan to eliminate the distinction between the top and main frames.

·         getNavigationKeyForService(service)

Answers the navigation key corresponding to the given service (string).  Since there may be several nav keys for the same service, the last one is answered.  You always get the correct response for tab menus.

·         getURL(navigationKey, withoutLanguage)

Answer the URL (string) corresponding to the given navigation key (also a string).  If the withoutLanguage boolean is false, the user’s language code is appended to the URL as a GET parameter.

·         getFieldLevelSecurityInfo(navigationKeyOrService)

Answer the field-level security meta-data for the tab menu given by the navigation key or service.  The result takes the form of a simple JavaScript object, with security types as properties, and values as arrays of all related authentication levels.  For example, assume that adjustment maintenance has field-level security defined for a user for security type “ADJAMT”, with two associated authentication levels, “1”, and “3”.  To retrieve the object defining all field-level security info for the service:

var info = top.getFieldLevelSecurityInfo(‘adjustmentMaint’)

or

var info = top.getFieldLevelSecurityInfo(‘CILAADUP’)

(Note the literal JavaScript representation of the result object would be {ADJAMT: [‘1’, ‘3’]})

To determine the array of authentication levels associated with this service and security type in one step:

            var authenticationLevels = top.getFieldLevelSecurityInfo(‘adjustmentMaint’)[‘ADJAMT’]

or

            var authenticationLevels = top.getFieldLevelSecurityInfo(‘CILAADUP’)[‘ADJAMT’]

·         getMain()

This heavily used function returns a reference to the window constituting the “main” iframe, containining cisMain.jsp and the core model.  The typical usage is top.getMain(), but many iframes define a global variable “main” for convenience.

·         tabMenu()

This function returns a reference to the current tabMenu iframe window.  The typical usage is top.tabMenu().  Many iframes define a global variable “topMenu” for convenience.

·         tabPage()

This function returns a reference to the current tabPage iframe window.  The typical usage is top.tabPage().  Many iframes define a global variable “topPage” for convenience.

·         model()

This convenience accessor method returns a reference to the core model held in the “main” frame.

·         openPage(navigationKey, tabName, keys, extraPageState, keepMemento, forceOpen)

This is a convenience function for the same function defined in cis.js.  See the description there for a fuller description.

·         getUser()

Returns the user id of the current user.

·         getUser()

Returns the user id of the current user.

·         getLanguage()

Returns the language code of the current user.

cis.js

The cis.js file contains the bulk of the core framework functions.  In addition to defining the major framework classes (CisModel, DataElement, List, etc.) it contains a number of important functions that are described here.  These functions are typically invoked by navigating to the main level, e.g. main.xyz(), where the global main has been defined to point to the frame containing cisMain.jsp.

·         array_remove_element(array, element)

Remove the indicated element from the given array.  Do nothing if it is not found.  If the element appears more than once in the array, remove only the first one.  Uses simple object comparison (==).

·         array_index_of(array, element)

Return the index (0-based) of the given element in the given array.  Answer –1 if the element cannot be found.  If the element appears multiple times, answer the first (lowest) index.  Uses simple object comparsion (==).

·         array_remove(array, index)

Compensate for missing functionality in the built-in JavaScript Array class in early versions of JScript.  Answers a new array instance with the element at the given index removed.  The length of the new array is one less than the length of the given one.

·         array_includes(array, element)

Answers a boolean indicating whether the given element is present in the given array.  Uses simple object comparison (==).

·         array_numeric_sort(array)

Sort the elements of the given array into numeric order, using a simple a < b comparison.

·         arrayDo(array, oneArgClosure)

Perform a loop over the elements of the given array, applying the function given by oneArgClosure to each element in turn.  The oneArgClosure function takes exactly one argument.  Extremely useful for signalling the intention of looping structures.  For example, consider this typical code:

var max = array.length;

for (var i = 0; i < max; i++) {

            var element = array[i];

            <do something to element>

}

This common structure can be simplified to the following:

arrayDo(array, oneArgFunction)

where oneArgFunction takes an array element as its parameter, and corresponds to the <do something to element> block above.

·         arraySelect(array, selectClosure)

Returns a new array consisting of elements from the given array that return true when applied to the selectClosure (a function that takes one argument).  For example, do this to find all numbers greater than 3 in an array:

var closure = function(each) {return each > 3};

var resultArray = arraySelect(array, closure);

·         arrayReject(array, rejectClosure)

Returns a new array consisting of all elements from the given array except those that return true when applied to the rejectClosure (a function that takes one argument).  For example, do this to find all numbers not greater than 3 in an array:

var closure = function(each) {return each > 3};

var resultArray = arrayReject(array, closure);

·         arrayDetect(array, detectClosure)

Returns the first element in the given array that returns true when applied to detectClosure, a one-argument function.  Return null if no element is found.

·         arrayDetectIfFound(array, detectClosure, doClosure)

Similar to arrayDetect(), but proceeds to execute the one-argument function doClosure with the detected element.

·         arrayDetectIndex(array, detectClosure)

Similar to arrayDetect(), but answers the index of the first element that satisfies the detectClosure function.

·         arrayCollect(array, collectClosure)

Return a new array consisting of the results of applying the collectClosure one-argument function in turn to each element of array.  For example, to double the values of an array holding numbers and store the result in a new array:

var closure = function(each) {return each * 2};

var resultArray = arrayCollect(array, closure);

·         arrayCopy(array)

Answers a new array holding the same elements as the given array.  Also known as a “shallow copy”.

·         arrayContains(array, detectClosure)

Returns a boolean indicating whether the given array contains an element that satisfies detectClosure, a one-argument function returning a boolean.

·         arrayUniquePush(array, object)

Similar to array_push, but skips appending the given object if an identical object is already present in the array.  Uses simple equality (==) for the comparison.

·         configureMainButtons()

Enable or disable the buttons on the main button bar according to the current state of the system.  This method should be triggered only if there is a reason to believe that circumstances have left the buttons in an inappropriate state.

·         openPage(navigationKey, tabName, keys, extraPageState, keepMemento, forceOpen, extraLoadKeys)

Navigate the system to the tab menu identified by the given navigationKey.  The particular tab page may be identified with the given tabName (string), which can also be identified as an integer index (0-based), otherwise the first tab page is used.  To display a particular object on the tabMenu/tabPage populate the keys parameter with a key/value object holding the logical keys that identify the object.  [The extraPageState parameter is deprecated].  The boolean keepMemento parameter identifies whether a memento should be stored for the current location, i.e. whether it will enter the history menu.  If not provided, it defaults to true.  The forceOpen boolean (defaults to false) controls whether the system should still show the tabMenu/tabPage if the desired object cannot be read (e.g. it was deleted from the database).  The optional extraLoadKeys object is merged with keys prior to performing the read query, allowing the addition or overriding of key values.

·         showMessageDescription(categoryNumberFieldName, messageNumberFieldName, shortDescriptionFieldName)

Open an alert dialog box showing a server message (the long message).  The category and message numbers are given indirectly via the categoryNumberFieldName and messageNumberFieldName, which are property names in the model from which the actual numbers are fetched.  An optional a short description field name can also be provided.  The alertClientWindow is a reference to a window object that should act as “host” to the alert dialog box, in order to keep focus on the correct window when the dialog is dismissed.

·         basicShowMessageDescription(categoryNumber, messageNumber, shortDescription, alertClientWindow)

Similar to showMessageDescription() as above, but directly accepts the desired category and message numbers rather than retrieving them from the model.

·         showErrorMessage(categoryNumber, messageNumber, clientSubstitutions, element, alertClientWindow)

Show the server error message identified by the given category and message numbers, and relating to the HTMLElement given as element (optional), which should receive focus.  The alertClientWindow parameter provides an optional reference to the “client” of this dialog that can be useful to preserve the correct focus when the dialog is dismissed.  The clientSubstitutions parameter is private.

·         restoreElements()

Frequently used in conjunction with setAvailableSignal(), this method clears flags that are used to implement synchronization control.

·         doSave(specialActionField, successFunction, forceSave)

Submit a change request to the server using the current model.  The specialActionField, if specified, is set to boolean true before submitting the model, without otherwise permanently changing the model.  Execute the given successFunction if the save operation succeeds.  A “clean” model is not submitted to the server unless the optional forceSafe boolean is true.  The browser will refresh itself to show the version of the model as returned from the server, unless the operation resulted in a warning or error.  In the first case a dialog shows the warning message line(s), and the user can choose to redo the save operation, ignoring further warnings.  In the second case a descriptive error dialog is shown.

·         doDelete()

Submit a delete request to the server using the current model.  Clears the window if the operation succeeds.

·         safelySetFocus(element)

Attempt to set focus to the given HTMLElement. 

·         updateElementFromModel(htmlElement)

Fetch the current value for the model attribute appropriate to the given htmlElement widget (using the name of the element) and display it.

·         setAvailableSignal(aWindow)

Configure the system to accept input after processing a request for the server.  Implemented by disabling the cisDisabled.css stylesheet.  Typically used in conjunction with restoreElements().

·         convertInternalDateTimeToLocal(value)

Convert the given internal date-time value to the user’s localized format and answer it.

·         convertLocalToInternal(htmlElement, value)

Using the datatype associated with the given html element (as described with its className), convert the given localized value into the internal system value and answer it.

·         convertInternalMoneyToLocal(value)

Convert the given money value (a String) into its localized representation and answer it.

·         updateField(event)

Update the value of the relevant HTML element based on the given event object.  This may involve side effects such as updating the model.

·         moneyToWholeInteger(amount)

This utility function helps you do simple arithmetic with money amounts.  Since the precision of SPL system monetary amounts can exceed that offered by the built-in JavaScript number types, we cannot perform arithmetic operations with those numbers without risking loss of precision and rounding problems.  The solution is to eliminate the decimal point (if any) in the amounts in order to yield “pure” integers, which admit to exact arithmetic (for addition, subtraction, and multiplication) to very high orders of precision.  The final result is then converted back into an internal money amount.

This function accepts an internal money amount (String) and returns a JavaScript “integer” representing the value.

·         wholeIntegerToMoney(integer)

Reverse the moneyToWholeInteger operation to yield a monetary amount corresponding to the given integer.

·         getInstallationData(key)

Returns the installation data (string) associated with the given key.

·         isUserModified()

Returns a boolean indicating whether the user has made any changes to the model that should be preserved (e.g. the system will issue a warning if the changes are not saved).