Skipjack Server Application Development Guide
Skipjack Server Application Development Guide
Skipjack Server Application Development Guide
http://205.177.219.178/doc/appDevGuide/printable.html
5 October 2002
Skipjack Server
Table of Contents
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. Model-View-Controller Approach Hello World Example Action Processing Database Processing Transaction Response Shell Script Processing Debug Management Error Checking Transaction Security Client Application Support a. Java Client Package b. Perl Client Package c. C++ Client Package 11. Event Listening
1 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
2 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
3 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
The Skipjack Server uses the HTTP protocol for all view-to-controller interactions, thereby allowing it to support the widest range of applications. As such, the server can accept inputs and generate outputs based on an arbitrary MIME data type. For example, the server can accept inputs using the standard URL-encoded data type and can generate text/html, text/plain, or image/gif formatted outputs, to name just a few. To support such a wide range of output formats, the Skipjack Server uses the Velocity Template Engine as the primary mechanism for formatting output files used to support a user interface view. Velocity supports a small number of easily-learned directives that facilitate formatting tasks, such as generating a tabular display of data or customizing the display for a given user. Since Velocity has a nominal set of logical expressions (such as the if statement) and allows access to Java object methods defined by the application, it could be used for both view and controller purposes. But, in practice, it is more effective to use Velocity solely as a view component whenever possible. In addition to Velocity, the Skipjack Server can use the output of any of transaction response item, operating system executable or custom application code as the output of a transaction. Such an approach is used, for example, to generate a chart image file based on the output of a database query. To facilitate custom application views, Skipjack Client Libraries exist for C++, Java and Perl applications, allowing them to easily interact with one or more Skipjack Servers. The Skipjack Client Library supports an object-based interface to the Skipjack Server, providing a set of method calls that shield the client application developer from all networking and formatting issues. See Client Application Support for more details about the available client libraries. Controller All controller tasks are performed in the Skipjack Server using a collection of transactions that are specified in Transaction Definition Files, which are formatted text files. Transactions typically consist of a collection of input validation, data model processing and output generation steps that result in the desired output file. Skipjack Server transactions are very flexible, require no programming skills, and can be easily modified as an application evolves. Although the Skipjack Server is based on the HTTP protocol, that does not limit it to supporting only browser-based views. In fact, the HTTP protocol is increasingly used to support server-to-server interactions, so-called Web Services. In a Web Services approach, one server sends a request to another server and receives a response. Skipjack Server transactions can be invoked using the HTTP protocol, supplying input parameters as either URL-encoded data (typical for browser-based views) or as XML-encoded objects (typical for Web Services views.)
4 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
The transaction definition file begins with three comment lines that specify the name of the file, followed by the include directive, that causes the item definitions specified in the skipjack-common.tdf file to be included in this transaction. (We'll discuss the common items in detail later.)
5 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Specifically, the NOP item definition (defined in skipjack-common.tdf) is used as the basis for the main item definition for this transaction. (Transaction item definitions can extend other item definitions, thereby inheriting all attribute definitions ... more on that later.) All transactions must define the main action item, which is the starting point for all transaction processing. In this example, since the main action item extends the NOP action, No OPeration is actually performed. After the main has been fully processed, the templateName attribute is used to determine the Velocity template file that will be used to generate the output for the transaction, which in this case is the ex1.vm template file. At that point, Velocity is used to merge the contents of the template file with the current context (as it is called by Velocity), and the resulting file is used as the transaction response. In this example, the ex1.vm template file consists of three initial comment lines (stripped out automatically by Velocity) and a single output line consisting of the words Hello World. When executed from a browser using the following url:
http://$SKIPJACK_URL/tutorial/helloWorld/ex1.tdf
It is important to note that even though this example is very simple, it demonstrates a pure Model-View-Controller application layout. In this case, the items defined in ex1.tdf represent the Controller, the formatting defined in ex1.vm represents the View, and no Model is needed.
which allows us to change the specific object/person to be greeted while retaining a uniform HTML format (e.g. white background, centered text, etc.)
6 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
In order to make use of this template, we need to supply a proper value for the $input.who context variable, when Velocity is called. That is, the transaction definition needs to specify a value for $input.who during its processing phase, before calling the Velocity template engine during the response generation phase. The simplest way to specify a value for $input.who is to define it explicity in the transaction definition file as follows (ex2.tdf):
// // ex2.tdf // include "skipjack-common.tdf" item input { who = "World"; } item main extends NOP { templateName = "ex2.vm"; }
where the input item definition has been added, with the who attribute being assigned a value of "World". In fact, any item attribute defined in the transaction file can be accessed by the Velocity template using the syntax $item.attr where item is the item name and attr is the attribute name. For example, the following transaction definition file and corresponding template file allows the background color and font type to be specified in the transaction file instead of being hardcoded in the template file:
// // ex3.tdf // include "skipjack-common.tdf" item input { who = "Fred"; } item myPage { bgColor = "yellow"; font = "h2"; } item main extends NOP { templateName = "ex3.vm"; }
7 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
There is nothing magic about the item names input, myPage, or even main with regards to Velocity variable substitution: any item attribute defined in the transaction definition file can be referenced in the Velocity template file used to generate the response for that transaction. The main item must always before defined for transaction processing purposes, even though it may not be referenced for Velocity processing purposes.) By judiciously using variables in Velocity templates that are defined in the transaction definition file, an application author can develop templates that can be used by a wider range of transactions. This directly supports the MVC approach, keeping the view (e.g., template format) separate from the underlying data model (e.g., item definitions). If you mis-type a variable name in a Velocity template or the corresponding attribute is not defined in the transaction definition file, then Velocity will not replace the variable at all and the variable notation will appear in the output file. Finally, it is important to note that Velocity provides a set of directives that allow variables to be defined and manipulated in the template file itself, including macro definition and expansion. A full explanation of such features will not be addressed here, although we will show some more complex Velocity examples where appropriate. Visit the Velocity Home Page (http://jakarta.apache.org/velocity) for complete documentation on all Velocity capabilities. When reading those documents with regard to Skipjack Server, simply remember that all of the items and attributes defined in a transaction definition file serve as the context in which the Velocity template is processed.
Attribute Expressions
Attribute values can be defined in a transaction definition file using simple assignment statements, as seen in the previous examples, or more complex Java-like expressions. For example, the following item definition:
item myPage { greeting = "Hello"; who = "World"; message = greeting + " " + who; }
results in a value of "Hello World" for the myPage.message attribute. In this case, the '+' operator is used to concatenate the values of the greeting and who attributes, with a space in between. No leading '$' notation is used to denote an attribute name in an attribute expression, as is required in a Velocity template.)
8 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
If you need to access an attribute from another item, then simply prepend the attribute name with the name of the item, as in the following example:
item input { who = "World"; } item myPage { greeting = "Hello"; message = greeting + " " + input.who; }
which references the who attribute from the input item while determining the value of the myPage.message attribute. The following operators (derived from C, Java, Perl, SQL, etc) are supported for attribute expressions: Operator +, -, *, / =, +=, -=, *=, /= <, >, <=, >=, !=, == =~, !~ ?: !, &&, || in, contains func(x,y,z) Meaning Arithmetic Assignment Comparison Regular Expression Match Conditional Unary Negation Boolean Comparison Membership Function Call
In addition to these operators, a list of values can be specified by surrounding them with the '[' and ']' symbols and separating each list value by a comma. For example, the following attribute expression defines a list of colors:
colorMap = ["red", "white", "blue"];
The various conditional operators, including the Comparison, Boolean Comparison, Regular Expression, and Membership operators, can be used in
9 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
conjunction with the ternary conditional operator '?:' to selectively assign a value to any attribute. For example, the following item definition:
item myPage { bgColor = (input.who == "Fred") ? "yellow" : "red"; }
defines the background color based on the current value of the input.who attribute. See TDF Operators for a complete description of all attribute expression operators and examples that make use of them. Attribute expressions can invoke a function with a variable set of input values and make use of the value returned in subsequent expression evaluation. For example, the following item definition:
item myPage { colorMap = ["red", "white", "blue"]; bgColor = element (colorMap, 1); }
extracts the second value "white" (zero-based indexing) from the colorMap attribute and assigns it to the bgColor attribute. See TDF Functions for a complete description of all currently supported functions and instructions for creating and installing new functions as needed. In some cases it is necessary to use the extensive power of Java objects and the Java runtime library to effectively determine the value of an attribute expression. Accordingly, attribute expressions can invoke arbitrary object and class methods, much like a function call. For example, the following item definition:
item myPage { colorMap = ["red", "white", "blue"]; colorIndex = integer(3 * class("java.lang.Math")->random()); bgColor = element(colorMap, colorIndex); }
will result in the background color being selected randomly from the colorMap list of colors, as can be seen by executing the ex4.tdf transaction. In this case, the class() function is used to access the "java.lang.Math" class and the -> operator is used to invoke the random() static class method to generate a random decimal number between 0 and 1. Then, the integer() function is used to convert the decimal number to an integer, so that it can be used by the element() function to extract the desired color from the colorMap list. See TDF Method Calls for a discussion of object and class method invocation in attribute expressions.
Input Parameters
It most cases it is beneficial to replace hard-coded variables in a Velocity template, with references to item attributes in the transaction definition
10 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
file. Similarly, it is usually beneficial to replace or overwrite hard-coded item attribute values with values that are provided by either the user or client application. That is, we want to allow the user or client to specify the input parameters for the transaction. Since Skipjack uses the HTTP protocol to invoke transactions, it is very easy for users to enter input parameters from a browser, simply by appending each parameter to the transaction URL as follows:
http://$SKIPJACK_URL/tutorial/helloWorld/ex4.tdf?who=Sally
which specifies the value "Sally" for the who input parameter. When the ex4.tdf transaction is executed by the Skipjack Server, it will automatically store any HTTP input parameter as a String attribute value in the input item, overwriting any similarly named attribute value that was defined in the TDF file itself. In this example, since the ex4.tdf transaction was defined as follows:
// // ex4.tdf // include "skipjack-common.tdf" item input { who = "World"; } item myPage { colorMap colorIndex bgColor font } = = = = ["red", "white", "blue"]; integer(3 * class("java.lang.Math")->random()); element(colorMap, colorIndex); "h2";
then the value "World" will be overwritten by the value "Sally" when the transaction is executed. In this way, transaction authors can specify hard-coded default values for all input parameters, allowing users or client applications to selectively overwrite any or all of them. All input parameter encoding must adhere to the standard HTTP encoding scheme in order to be properly processed when executing transactions from a browser or client application. In general, since the input item is used to automatically store all input parameters for a transaction, it shouldn't be used to store other internal attributes that may be needed to perform certain actions. Any appropriate HTML form can also be used to submit input parameters to a transaction. The Skipjack Server supports both the GET and POST
11 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Input Validation
Any time a user or client application is allowed to submit input parameters, it is generally prudent to perform some type of syntax and/or semantic validation on those inputs. To that end, the inputSchema item can be defined for a transaction that specifies the name, type and mode of each input parameter supported by that transaction. For example, consider the following transaction definition (ex5.tdf):
// // ex5.tdf // include "skipjack-common.tdf" schema inputSchema extends BaseSchema { attrs = [ ["who", "string", "default", "World"], ["fontSize", "integer", "default", 2], ["bgColor", colorMap, "default", "white"] ]; colorMap = ["red", "white", "blue"]; } item myPage { bgColor font } = input.bgColor; = "h" + input.fontSize;
By default, if no parameters are given, then this transaction produces a result similar to the previous examples, printing "Hello World" on a white background. However, this transaction also allows the user or client application to define the who, fontSize and bgColor input parameters to control the type of output that is generated. All aspects concerning the supported input parameters are defined in the inputSchema item, which we will now explain in detail.
12 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
where "name" is the parameter name and "type" is the parameter type. (See Schema Class for a list of all parameter types supported.) The third element indicates if the parameter must be specified (ie. "nonempty") or is optional. If "default" is specified, then the "default value" will be used automatically if the user or client application doesn't specify a value for that parameter. If "optional" is specified, then no value is specified automatically. (The "optional" setting is used primarily for documentation purposes as will be discussed later.) In the example above, the first input parameter is defined with the name "who" and a type of "string". That is, the who input parameter can consist of an arbitrary string for which no syntax checking will be performed. Since the who parameter is marked as a "default" parameter, it will be assigned the value of "World" if it is not otherwise defined by the user or client application. The fontSize input parameter is defined as an "integer" value, which means that a syntax check will be performed automatically before the transaction is executed and that the value will be converted to a Java Long object after successful validation. Finally, the bgColor input parameter type is a reference to the colorMap attribute, which itself is defined as a list of values. Any time that a list of values is specified as the input parameter type (either as an embedded list in the parameter definition tuple or a reference to a list value attribute), then the input parameter value must be contained in that list to be valid. Based on the inputSchema definition, all of the following transaction URLs are valid:
ex5.tdf ex5.tdf?who=Sally ex5.tdf?bgColor=red ex5.tdf?who=Sally&bgColor=blue&fontSize=1 ex5.tdf?ignore=this
and all of the following URLs will result in an error message being generated:
ex5.tdf?bgColor=green ex5.tdf?fontSize=not-a-number
13 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
For example, the following format expression could be used to ensure that the fontSize parameter is not greater than 32:
fontSizeFormat = _srcItem.fontSize < 32 ? _srcItem.fontSize : 32;
Input/Output Separation
Note how the myPage item attributes have been re-defined in terms of the input item attributes so that they can be referenced by the same Velocity template as before. In general, it is prudent to create separate items like myPage that are used to specify formatting or data values referenced within a Velocity template file, even if they need to trivially reference values from other items, like the input.bgColor reference. By defining a separate item, the transaction author is clearly differentiating between the transaction input parameters and the attribute values required by the template being used. Essentially, the myPage item defines the "inputs" specifically used by the template. This allows templates to be easily used across a number of transactions simply by defining the myPage item (or any name of your choosing) in each such transaction, specifying the attribute value mappings appropriate for that transaction. For example, some transactions may pass the values through from the input item, like the example above, while others hard-code myPage values or extract them from the database. (Later, we will discuss how the TDF "include" and "overrides" directives can be used to create a single item that is shared across all such transactions, requiring only individual attributes to be overwritten.)
14 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
The default form displays the transaction name, the input fields, and a set of buttons to submit, reset or cancel the transaction. The default form is based on a collection of Velocity macros that are deployed as part of the standard Skipjack components and there are a number of attributes that can be specified to alter the appearance of the form and individual fields. See HTML Form Macros for more details. Clearly, the default input form is no substitute for a well-designed HTML-based user interface, where form fields and buttons are properly placed to reduce screen clutter and make it easier for the user to enter critical information. Nonetheless, when developing and/or trouble-shooting transactions and applications, the default form provides a very convenient mechanism for executing any transaction.
15 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
Note, we need to include the skipjack-common.tdf file before extending the CopyAction item, because it is defined in that file. After defining the copyInputToOutput action, we only need to "execute" it as part of the main action. In the Hello World examples, the main item extended the NOP action, which doesn't do anything. Therefore, if we want to execute the copyInputToOutput action, we need to extend a different action. Generally, the main item extends the BaseAction as in the following definition:
16 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
The BaseAction executes all of the action items listed by the actions attribute, which in this case is only the copyInputToOutput action. Actions are executed sequentially by the BaseAction until all actions are done or an exception is thrown. If all actions complete successfully, then the
main.templateName
attribute is used to determine which Velocity template to use to generate the transaction response. In this case, we can assume that the "output.vm" template references the output item attributes to format an appropriate response. (Otherwise, why else did we copy them there?) In simplest terms, actions embody the essential Unix concept of using pipelines of small, flexible, building blocks to perform complex functions. Over time, evolution yields a powerful set of actions that can handle most application needs with little additional programming. Transaction definition files simply define what actions are required and what inputs to provide to them, while the actions actually perform the work. As in many tutorials, we have a "chicken-and-egg" problem in that we need to discuss action processing before we can describe the actual actions themselves. So, for the remaining examples in the section, all of the actions referenced are "abstract actions" (ie. they don't really exist.) In subsequent sections, we will discuss the real actions provided by Skipjack for various processing tasks.
Undo Actions
If an error occurs while processing any of the action items referenced by the actions attribute, then all subsequent actions defined in that list will be automatically ignored. In such cases, if the undoActions attribute references one or more actions, then each of those actions will be processed sequentially. Undo actions can be used to restore partially completed changes, execute custom error processing, record custom debug information or perform any task that is required to compensate or handle the original action error. For example, the following action item attempts to insert records into two different tables:
item insertRecords extends BaseAction { actions = ["insertOne", "insertTwo"]; undoActions = ["deleteOne", "deleteTwo"]; }
If either insert action fails, then the undo action is called to remove both records. If one of the undo actions fail, then the remaining undo actions are ignored and the original exception is used to generate the transaction status and error message.
17 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
The actions referenced by the "undoActions" attribute don't necessarily have to "undo" anything, they can be used, for example, to simply log a problem or inform an operator about something that needs to be examined. Prior to executing any undo actions, the tx.errorMsg, tx.errorClass and tx.errorAction attributes will be assigned values that can be used by any of the undo actions to determine what failed in the action list. The tx.errorMsg and tx.errorClass attributes will contain the message and class name of the Java exception that was generated by the action that failed, while the tx.errorAction attribute will reference the Java Item object for the action that failed. For example, the following undo action generates a message based on the name of the action that failed and the error message generated by that action:
item genMessage extends BaseAction { msg = "Action " + tx.errorAction->getName() + " Failed With The Message '" + tx.errorMsg + "'"; }
In addition to the transaction attributes described above, undo actions may also be able to use the tx.status attribute to determine the appropriate processing to be performed. The tx.status attribute is defined by most, but not all, actions when they throw an exception.
Done Actions
Regardless of the success or failure of the actions referenced by both the actions and undoActions attributes, one or more actions can be specified by the doneActions attribue that are executed after the other actions. Generally, such actions are used to release resources or record processing information despite failures. Done actions can be used with or without undo actions, depending on the current task to be performed. For example, the following action locks a table before inserting two records and then unlocks the table:
item insertRecords extends BaseAction { actions = ["lockTable", "insertOne", "insertTwo"]; undoActions = ["deleteOne", "deleteTwo"]; doneActions = ["unlockTable"]; }
The table lock is released by an action specified in the doneActions attribute to ensure that the table is unlocked regardless of the outcome of the insert and/or delete actions.
18 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
As with the actions and undoActions attributes, all of the actions listed by the doneActions attribute are processed sequentially. If an error is generated by one of the doneActions then all remaining actions will be ignored. If no error was previously generated by one of the actions defined by the actions attribute, then the error generated by the action in the doneActions list will be used as the error result for the overall action, causing the transaction to be aborted in normal operation. Developers familiar with the Java "try-catch-finally" language construct may note the similarity between that construct and the actions, undoActions and doneActions attributes. Although the specific semantics are slightly different, the overall concepts are the same.
Action Locks
In any multi-threaded server such as Skipjack, certain types of concurrent processing tasks may result in inconsistent behaviors, depending on the order in which they are performed or other types of race conditions. In order to help eliminate such problems, Skipjack allows transaction authors to allocate one or more resource locks to any action. Once allocated, a resource lock prevents all other actions from accessing the same lock until it is released by the original action. Resource locks are allocated simply by listing the name of each lock required and are automatically released when the action has been completed. Resource locks remain allocated for all sub-actions performed by a given action. The following attributes can be defined for any action to specify which resource locks are needed and how to handle any locking errors: Attribute
_locks
Description Optional list of resource lock names that must be acquired before action processing begins. This can be used to prevent other actions or transactions from interfering with the processing of the current action. Lock acquisition is attempted after any background thread is created (see Background Actions below for details), but before any preCondition check that may be specified (see Action Pre-Conditions below for details). All resource locks acquired by the current action will be automatically released after all action processing is complete, including any exception processing. Optional timeout (in milliseconds) to be used when attempting to acquire one or more resource locks as specified by the _locks attribute. If any of the locks cannot be acquired in the specified time period, then an exception will be thrown and the action will be aborted, releasing any locks that were actually acquired. If a zero or negative value is defined, then no timeout will occur. The default value is 60 seconds. Optional error message to be used if the _lockTimeout expires. Prior to accessing this attribute, the values of the _lockTimeoutLock, _lockTimeoutThread and _lockTimeoutAction attributes will be assigned the name of the lock that caused the timeout, as well as the name of the thread and action that currently holds that lock. These attributes can be used to generate a custom timeout message and/or to check for deadlock conditions. Optional error status to be used if the _lockTimeout expires.
_lockTimeout
_lockTimeoutMsg
_lockTimeoutStatus
19 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
For example, the following action definition requires the Device12 and ChangeStatus resource locks to be allocated before the configDevice and resetDevice actions are executed. If either of the locks cannot be allocated within 10 seconds, then a custom error message is generated.
item doWork extends BaseAction { _locks = ["Device12", "ChangeStatus"]; _lockTimeout = 10000; _lockTimeoutMsg = "Unable To Get Lock " + _lockTimeoutLock + " Because It Is Currently Owned By " + _lockTimeoutAction; actions } = ["configDevice", "resetDevice"];
Action Errors
In normal operation, if any action generates an error then all subsequent action processing is aborted and an unsuccessful transaction response is generated. This is done to prevent non-deterministic side-effects from subsequent actions that may depend on the outcome of the failed action and to make it easier for transaction authors to trouble-shoot transaction failures. In some situations, however, it may be necessary or desirable to ignore any error generated by a given action. For example, an action may attempt to update a record that may or may not exist. In such cases, a transaction author may override the normal error processing behavior for that action by setting the _ignoreError attribute to true. When set to true, any error generated by that action will be ignored and transaction processing will continue with the next action to be processed. When the error is ignored, the current values of the tx.status and tx.errorMsg attributes will be copied into the tx.ignoredStatus and tx.ignoredErrorMsg attributes, respectively, for subsequent use by the transaction. Then, the tx.status and tx.errorMsg attributes will be reset to the values that were set just prior to the action being processed. If the _ignoreError attribute is used for an action that is inside of a loop, the transaction author is responsible for clearing the tx.ignoredStatus and tx.ignoredErrorMsg attributes as needed. The _ignoreError attribute does not affect the way that errors are handled while processing the actions, undoActions, and doneActions lists, only the way the overall action error is handled by the transaction. If a transaction author wants to ensure that a list of actions are performed regardless of errors generated by any of those actions, then the _ignoreError attribute should be set to true in each of those actions, not in the action that defined the list of actions. For example, the following action defines the same behavior as in the previous example, but uses the _ignoreError attribute to continue transaction processing regardless of any error generated by this action:
item insertRecords extends BaseAction {
20 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
_ignoreError = true; actions undoActions doneActions } = ["lockTable", "insertOne", "insertTwo"]; = ["deleteOne", "deleteTwo"]; = ["unlockTable"];
Alternatively, the following action attempts to delete two records, ignoring errors generated by either action:
item deleteRecords extends BaseAction { actions = ["lockTable", "deleteOne", "deleteTwo", "unlockTable"]; } item deleteOne extends MyDeleteAction { _ignoreError = true; .... } item deleteTwo extends MyDeleteAction { _ignoreError = true; .... }
In this example, the _ignoreError attributes of the underlying delete actions are set to true, causing any errors generated by those actions to be ignored. Consequently, the actions performed by the higher level deleteRecords action can be specified solely within the actions list, since the delete actions can not cause the list processing to be aborted.
Action Pre-Conditions
There are times when an action should be executed only if a certain pre-condition is true. All Skipjack actions support this feature through the use of the preCondition boolean attribute. Specifically, if the preCondition expression evaluates to "true", then the action will be executed. Otherwise, the action will not be executed and the transaction will continue processing the next action, if any exists. For example, suppose in the previous example that we want to delete the second record only if the input parameter RecordCount is greater than one. That could be specified using the following preCondition expression:
item deleteRecords extends BaseAction { actions = ["lockTable", "deleteOne", "deleteTwo", "unlockTable"]; } item deleteTwo extends MyDeleteAction { preCondition = integer(input.RecordCount) > 1; .... }
21 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Alternatively, if the records should only be deleted if the RecordCount is greater than zero, then we could add a preCondition expression to the top-level action as follows:
item deleteRecords extends BaseAction { preCondition = integer(input.RecordCount) > 0; actions = ["lockTable", "deleteOne", "deleteTwo", "unlockTable"]; } item deleteTwo extends MyDeleteAction { preCondition = integer(input.RecordCount) > 1; .... }
In this case, the deleteRecords.preCondition expression determines if any of the sub-actions should be performed, while the deleteTwo.preCondition expression determines if the deleteTwo sub-action should be executed.
item logMsg1 extends logMsg { Message = "Transaction Started"; } item logMsg2 extends logMsg { Message = "Transaction Still Running"; } item logMsg3 extends logMsg { Message = "Transaction Completed"; } item main extends BaseAction { actions = [ "logMsg1", ..., "logMsg2",
22 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
..., "logMsg3" ]; }
In this case, an action item must be created for each message, with the Message attribute being overwritten in each case. Although item inheritance makes this a relatively straight-forward process, it still requires the author to invent unique names for each item and it otherwise clutters up the transaction definition file. To alleviate such problems and to allow action items to be easily re-used, actions can be invoked using an action argument list, An action argument list consists of a Vector of values, where the first value is interpreted as the action name, followed by one or more (attribute, value) pairs. For each pair of values specified, the action item attribute will be temporarily overwritten with the value given for the duration of the action execution. After the action processing has been completed, the overwritten attribute values will be restored. Essentially, this allows actions to be executed as "subroutines" or "methods" with certain values supplied as "input" parameters. For example, the previous example could be re-written as follows using action arguments:
item logMsg extends Severity = LogFile = Message = } LogAction { "INFO"; "navius.skipjack.errorlog"; "Unknown"; { "Message", "Transaction Started"), "Message", "Transaction Still Running"), "Message", "Transaction Completed")
item main extends BaseAction actions = [ ("logMsg", ..., ("logMsg", ..., ("logMsg", ]; }
In this case, the logMsg action itself is invoked three times, overwriting the Message attribute of that action item every time. Any Vector value specified in an action list will be interpreted as an action argument list, using the first element as the action name and all subsequent pairs of elements as the (attribute, value) pairs. Any number of attribute value pairs can be specified when executing an action, provided that the attributes already exist in the action item. For example, the Severity attribue may be overwritten for the second message using the following definition:
item logMsg extends Severity = LogFile = Message = LogAction { "INFO"; "navius.skipjack.errorlog"; "Unknown";
23 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
} item main extends BaseAction { actions = [ ("logMsg", "Message", "Transaction Started"), ..., ("logMsg", "Severity", "DEBUG", "Message", "Transaction Still Running"), ..., ("logMsg", "Message", "Transaction Completed") ]; }
Since all of the logMsg attributes are automatically restored after being invoked by each action argument list, the third message will still be assigned a Severity level of "INFO", which is the value defined for that attribute in the logMsg action item. Action items that have their _readonly attribute set to "true" cannot by invoked using an action argument list. Furthermore, all of the attribute names specified in the action argument list must already exist in the action item and an attribute value must be specified for each attribute name given (ie. the argument list must have an odd number of entries). If any of these conditions fails, an appropriate error will be generated.
Background Actions
In most transactions, all of the actions defined in the main.actions are sequentially executed to completion before the transaction response is generated. However, there are times when it is desirable to execute one or more actions in independent threads, thereby allowing them to be processed concurrently while the rest of the transaction is being processed or well after the transaction has generated its response. The Skipjack Server supports such a processing style through the use of background actions, which can be configured using the following collection of attributes that are supported for all actions: Attribute
_bgAction
Description Optional boolean value that indicates if the action should be processed as a background thread. If true, a background thread is created, the action is processed by that thread and the transaction continues processing all subsequent actions. The Process ID for the background thread is stored as the value of the _bgProcessID attribute of the destination item for this action, which can be
24 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
By default, any exception thrown by a background action will be printed to the navius.skipjack.errorlog. If this attribute is defined, it specifies the name of the log file to be used. By default, the status of the background process will be automatically updated as it proceeds. If this attribute is set to false, then no status updates will be performed. In either case, transactions can use ProcessAction to update the process status as needed, given the _bgProcessID attribute value. Optional string value that will be assigned as the name of the background thread in the process table. If this attribute is not defined, the action identifier will be used. Specifies how action processing should be triggered, including types 'REPEAT' or 'ALARM'. If 'REPEAT' is specified, the action will be processed one or more times depending on the _bgRepeatCount attribute, with an optional delay between each processing cycle, as specified by the _bgRepeatDelay attribute. If 'ALARM' is specified, the action will be processed for each chronological alarm specified by the _bgAlarm* attributes. The default value is 'REPEAT'.
_bgAutoStatus
_bgProcessName
_bgTrigger
_bgRepeatCount
Specifies the number of times that the action should be executed when _bgTrigger is 'REPEAT', with a default value of 1. If a negative or zero value is specified, then the action will be repeated until the background action process is stopped via the Process Manager. Prior to each execution of the action, the _bgCount attribute of the action's destination item will be set to the current iteration count (if the destination item is writable). The iteration count starts with 1 and automatically rolls over after Integer.MAX_VALUE is reached.
_bgRepeatDelay
Specifies an optional sleep time (in seconds) between repeated action executions. If the value is less than or equal to zero, no delay will be inserted between action executions. The default value is 2.
25 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
_bgAlarmDays
An optional Vector of integers from 1 to 31 that designates each day of the month that an action should be executed when 'ALARM' is specified for the _bgTrigger attribute. If this attribute is not defined or the Vector is empty, then the action will be executed every day of the month. This attribute should not be specified if the _bgAlarmWeekdays attribute is specified.
_bgAlarmWeekdays
An optional Vector of integers from 1 to 7 (1=Sunday, 2=Monday, ...) that designates each day of the week that an action should be executed when 'ALARM' is specified for the _bgTrigger attribute. If this attribute is not defined or the Vector is empty, then the action will be executed every day of the week. This attribute should not be specified if the _bgAlarmDays attribute is specified.
_bgAlarmHours
An optional Vector of integers from 0 to 23 that designates each hour of the day that an action should be executed when 'ALARM' is specified for the _bgTrigger attribute. If this attribute is not defined or the Vector is empty, then the action will be executed every hour of the day. An optional Vector of integers from 0 to 59 that designates each minute of the hour that an action should be executed when 'ALARM' is specified for the _bgTrigger attribute. If this attribute is not defined or the Vector is empty, then the action will be executed every minute of the hour.
_bgAlarmMinutes
One-Time Actions
By default, if the _bgAction attribute is set to 'true', then the current action will be processed once as a background action. As soon as the background action is launched, the transaction will continue processing any subsequent actions and/or will generate the transaction response. Such an approach is commonly used for actions that may require a significant amount of time to complete and the transaction author does not want the client to wait until it is done. Any type of action can be executed as a background action. For example, the following action item definitions results in a background action that simply appends a message to the error log (using the LogAction):
item myBackgroundAction extends BaseAction { _bgAction = true; actions = ["logMsg"]; } item logMsg extends LogAction {
26 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Repetitive Actions
Background actions can be repeated 2 or more times, by specifying the _bgRepeatCount attribute, or indefinitely by setting that attribute to zero or a negative value. A delay (in seconds) between each action execution can be specified using the _bgRepeatDelay attribute. For example, the following modification to the previous example results in myBackgroundAction being executed a total of 20 times, with a 5 second delay in between each execution:
item myBackgroundAction extends BaseAction { _bgAction = true; _bgRepeatCount = 20; _bgRepeatDelay = 5; actions = ["logMsg"]; } item logMsg extends Severity = LogFile = Message = } LogAction { "INFO"; "navius.skipjack.errorlog"; "Background Processing Performed";
Such an approach might be used for delivering information to an external system, attempting delivery only a certain number of times before eventually giving up, as in the following example:
item myDeliveryAction extends BaseAction { _bgAction = true; _bgRepeatCount = 3; _bgRepeatDelay = 60; doneFlag preCondition actions } item doDelivery extends DeliveryAction { _formatType = "FILE"; fileInputFile = "/tmp/myData.txt"; _transportType ftpOutputHost ftpOutputUsername ftpOutputPassword ftpOutputFile } = "FTP"; = "somewhere.com"; = "somebody"; = "easy to guess"; = "remoteFile.txt"; = false; = !doneFlag; = ["doDelivery", "setDoneFlag"];
27 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Notice how the preCondition attribute of the myDeliveryAction action is used to abort subsequent action processing after the setDoneFlag action has been executed, which can only happen if the doDelivery is successfully executed. In this example, even if the doDelivery is successful the first time, the myDeliveryAction will be repeated two more times over the next two minutes. However, it won't actually do any processing because the preCondition will be false.
Periodic Actions
Background actions can be performed on a periodic basis (e.g., hourly, daily, weekly, etc.) by setting the _bgTrigger attribute to 'ALARM' and by specifying one or more alarm times using the _bgAlarm* attributes. The action will be performed once for every time specified and will be repeated indefinitely until the action is stopped by the Process Manager (see below for details). For example, the following background item definition causes the logMsg action from the previous example to be executed every quarter hour:
item myPeriodicAction extends BaseAction { _bgAction = true; _bgTrigger = "ALARM"; _bgAlarmMinutes = [00, 15, 30, 45]; actions = ["logMsg"]; }
If such action processing is only desired during normal business hours (9am-5pm), then the _bgAlarmHours attribute can be specified as in the following example:
item myPeriodicAction extends BaseAction { _bgAction = true; _bgTrigger = "ALARM"; _bgAlarmHours = [9, 10, 11, 12, 13, 14, 15, 16]; _bgAlarmMinutes = [00, 15, 30, 45]; actions = ["logMsg"]; }
In this case, the action will be executed 32 times per day, 4 times each hour, from 9:00am to 4:45pm. Since the _bgAlarmDays and the _bgAlarmWeekdays attributes were not specified, the action will be executed every day of the month.
28 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Background actions are processed by server threads that are dynamically created as needed. If the background action terminates normally, such threads will also terminate normally, releasing any server resources that have been used by the transaction from which the action was launched. However, since repetitive and periodic background actions may never terminate, it may be necessary to monitor their progress and/or terminate them manually. In general, it is best to design periodic or repetitive background actions to print information to an appropriate log for trouble-shooting purposes, but that is not a requirement. All background action threads are automatically registered in the Skipjack Server's Process Table when they are created and un-registered when they are terminated. The Process Table can be viewed using the Skipjack console to monitor the status of a background action and/or to terminate the process manually. (An error message will be appended to the error log associated with any background action that is terminated manually.) See Process Manager for more information about the Process Manager and the Process Table.
29 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
30 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
that retrieves the current list of databases defined on the database server. The actual SQL expression is defined in the "getDBList.sql" as follows:
## ## getDBList.sql ## show databases
which is processed as a Velocity template file (even though this specific script doesn't really use any Velocity features). After the getDBList action is executed, the following attributes will be automatically defined:
getDBList.sqlExpr getDBList.table getDBList.rowCount getDBList.rows getDBList.cols getDBList.xxxCol = = = = = = SQL expression sent to database server query results number of rows in table Vector of TableRow objects in table Vector of TableCol objects in table TableCol object for column xxx
31 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Database List
authority mysql pls security skipjack test
The template file used in this example demonstrates how the #foreach() directive can be used in Velocity to easily iterate over a Vector of data. (Actually, any enumerable Java object can be iterated over using the #foreach() directive.) Velocity assigns each TableRow object defined in that list to the $row variable, which is then used to access the getValue() method defined by the TableRow object. The ability to execute any public method allows Velocity templates to handle a wide variety of formatting problems, as well as make decisions about the content that should be displayed. Clearly, such power can be quickly abused, turning Velocity templates into mind-boggling, hard-to-read, coding nightmares if you are not careful. But, when used in moderation, it can make complex formatting tasks much simpler. The primary drawback of such an approach is the need to understand which Java methods are available, requiring access to the Java documentation for key objects. For that reason, a complete set of Java documentation for all objects used by Skipjack and the underlying toolkit can be found at:
http://$SKIPJACK_URL/apidoc/
including the table-related objects defined in the navius.toolkit.table package. When executing Java methods from Velocity templates, it is important to remember that any exception generated by such methods are "consumed" by Velocity without notification. In such cases, the corresponding variable substituion is aborted, resulting in the variable expression itself being passed to the output stream. See Debug Management for Skipjack mechanisms that can be used to trouble-shoot such problems.
32 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
attrs = [ ["dbName", dbList, "default", "mysql"] ]; dbList = ["mysql", "skipjack", "test"]; } item getTableList extends SQLTableAction { sqlFile = "getTableList.sql"; } item main extends BaseAction { actions = ["getTableList"]; templateName = "ex2.vm"; }
allows the user to specify a database name and then it retrieves the list of all tables in that database using the following SQL script (getTableList.sql):
## ## getTableList.sql ## use $input.dbName; show tables;
In this case, the input.dbName attribute is used to dynamically construct the desired SQL expression. In subsequent examples, we'll see how the #foreach() directive can be used to easily generate a series of SQL insert statements. We can exercise this transaction using the default input form: http://$SKIPJACK_URL/tutorial/dbQuery/ex2.form which will show the list of database tables, as well as the actual SQL expression used based on the following template file (ex2.vm):
// // ex2.tdf // include "skipjack-common.tdf" schema inputSchema extends BaseSchema { attrs = [ ["dbName", dbList, "default", "mysql"] ]; dbList = ["mysql", "skipjack", "test"]; } item getTableList extends SQLTableAction { sqlFile = "getTableList.sql"; } item main extends BaseAction {
33 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
In this template, we used a more complicated variable reference: $getTableList.table.getCol(0).getName() to access the getCol() method of the Table object, and then the getName() method of the resulting TableCol object to get the name of the column.
Table Formatting
As we've seen, it is relatively easy to create a transaction that accepts a number of different input parameters, executes a dynamic SQL expression and returns the results. Once such a transaction exists, it can be used by GUI and non-GUI applications alike to perform that function. For GUI applications, however, more sophisticated output formatting is usually needed, and that is where Velocity begins to shine. Consider the following transaction definition file (ex3.tdf):
// // ex3.tdf // include "skipjack-common.tdf"
34 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
schema inputSchema extends BaseSchema { attrs = [ ["dbName", dbList, "default", "mysql"], ["tblName", "string", "nonempty"] ]; dbList = ["mysql", "skipjack", "test"]; } item getTableSchema extends SQLTableAction { sqlFile = "getTableSchema.sql"; } item main extends BaseAction { actions = ["getTableSchema"]; templateName = "ex3.vm"; }
Such a database query returns a table with multiple rows and columns that we would like to format using a standard HTML table tag. The following Velocity template file performs such a formatting task:
## ## ex3.vm ## <body bgcolor="white"> <center> <h2>Table Schema For '${input.dbName}.${input.tblName}'</h2> <table> <tr bgcolor=#efefef> #foreach ($col in $getTableSchema.cols) <th>$col.getLabel()</th> #end </tr> #foreach ($row in $getTableSchema.rows) <tr> #foreach ($col in $getTableSchema.cols) #if($row.getValue($col.getName())) <td>$row.getValue($col.getName())</td> #else <td>NULL</td> #end #end </tr> #end </table> </center>
35 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Clearly, this template is more complicated than we've seen before, but is still constructed from only a couple of directives. Specifically, the #foreach() directive is used to loop over all column labels to create the table column headers. Then, nested #foreach() directives are used to loop over each row and each column therein, to display each of the table cells. Finally, the #if() directive is used to determine when a NULL value exists in a table cell, replacing it by the string "NULL". (If Velocity generates a NULL value while performing a variable substitution, then it assumes that the variable is not defined and does not replace it in the output stream. The #if() directive can be used to detect such a situation and generate an appropriate output string.)
which references the table retrieved by the getTableSchema action item and defines a color map for the rows that are displayed in the table. By defining that item in the transaction definition file, we can replace the previous template file by the following template file (ex4.vm):
## ## ex4.vm ## <body bgcolor="white"> <center> <h2>Table Schema For '${input.dbName}.${input.tblName}'</h2> #listShow($tblFormat) </center>
which uses the #listShow() macro to display the table. The #listShow() macro is defined in the following files:
$SKIPJACK_HOME/app/macros/list-macros-01.vm $SKIPJACK_HOME/app/macros/html-macros-01.vm
which is a self-documenting file. The documentation for all the macros defined in that file can be viewed using the following URL:
36 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
http://$SKIPJACK_URL/MACROS/list-macros-01.vm http://$SKIPJACK_URL/MACROS/html-macros-01.vm
which also has the side-effect of reloading the macro definitions, if you need to make modifications to the macros without rebooting the server. A number of macros defined in those files are useful for generating common HTML displays, such as a shaded box surrounding other text, titles, section headers or error messages. There is also a macro for generating an HTML-based barchart for a table with numeric columns, using item attributes defined in the transaction definition file, much like the #listShow() macro.
and the following transaction excerpt that uses the getUserRecord action to retrieve the data record associated with a given user:
item getUserRecord extends SQLRecordAction { sqlFile = "getUserRecord.sql"; UserName = "Fred"; _destItem = "userRecord"; }
Since the _destItem attribute is defined, the resulting values will be stored in the "userRecord" item. If no such item exists when the action is executed, it will be created automatically. After the getUserRecord action is executed, the following item attributes might hold the values retrieved from the database for user "Fred":
userRecord.UserName = "Fred" userRecord.Password = "asdasd8a"
Such information could then be used by another action, such as the following:
37 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
that might retrieve corresponding information from another table, using the UserName and Password values returned by the previous action as input parameters for the getUserAccount.sql script. Note again how the _destItem attribute is used to store the results of the second SQL query in the same item as before, resulting in the following attributes being defined:
userRecord.UserName userRecord.Password userRecord.Salary userRecord.Bonus userRecord.ExpenseLimit
Clearly, the results of the SQLRecordAction, like any other item, can be used by an output template to format the detailed information for a given user.
would retrieve all of the user names and store them as a list of values assigned to the userList.UserName attribute:
userList.UserName = ["Fred", "Sally", "Jim"];
38 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Database Update/Insert
All of the examples so far have focused on retrieving data from the database, but the SQLTableAction can also be used to execute one or more SQL update, delete and insert statements in a SQL script. In such cases, the resulting rowCount will reflect the number of updates performed, not the number of rows in the table, which will be empty. There are also situations when a record needs to be added to a database table or updated if it already exists. To easily support such operations, the
SQLUpdateAction
action can be used to specify separate update and insert SQL scripts, defined as the "updateSql" and "insertSql" attributes (instead of the "sqlFile" attribute used for other SQL actions). The SQLUpdateAction first attempts to execute the "updateSql" script. If that succeeds and more than one row is updated, then no further processing is done. If the "updateSql" fails to update any rows in the table, then the "insertSql" script is executed. In either case, if an exception is generated, the action is aborted. For example, the following action:
item updatePassword extends SQLUpdateAction { updateSql = "password-update.sql"; insertSql = "password-insert.sql"; UserName = "Fred"; Password = "changethis"; }
// // // insert values )
39 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
attempts to update the password for 'Fred' using the password-update.sql. If no such record exists, the password-insert.sql script is executed to create the user record and set the password.
When the getUser action is executed, it will acquire a single database connection that is then shared by all of the actions executed by that action. When the getUser action is done, the database connection will be released. In databases such as MySQL, this approach can be used to lock one or more tables, and then to release the locks after all actions have been performed.
40 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
To support such an approach, the $curAction Velocity variable can be used to reference the action item that caused this template to be processed. For example, consider the following SQL template file (getUser.sql):
select * from myUsers where UserName = '$curAction.UserName'
Both action items reference the same SQL script file, while defining different UserName attributes. When the getFred action is executed, the $curAction variable in the SQL script file will reference the getFred item and the value of $curAction.UserName will be "Fred". On the other hand, when the getSally action is executed, the $curAction variable will reference the getSally item and the value of $curAction.UserName will be "Sally". By using the $curAction variable in SQL script files as much as possible, and by defining all required SQL input attributes in the same action item, the mapping between SQL action items and SQL script files is greatly simplified and there is a greater chance that scripts (as well as SQL action items) can be re-used by other transactions. When generating SQL expressions, it is important to remember the string quote conventions used by SQL and to use the proper quote symbols for each data value. To facilitate such processing, the #sqlString() macro can be used to properly quote a string value. For example, the previous example script file is best written as follows:
select * from myUsers where UserName = #sqlString($curAction.UserName)
to ensure that the $curAction.UserName value is properly quoted. The #sqlString() macro is designed to support the string quoting conventions used by MySQL and other ANSI SQL database servers. Different servers may require different quote conventions.
41 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Column Labels
Since database column names are often cryptic and may appear in a wide-range of displays, the SQL actions are designed to automatically determine column labels that are more appropriate for user displays. We've already seen an example of this when using the getLabel() method from the TableCol object as the column label in an HTML table. By default, the column label is defined to be the same as the table column when a table is retrieved from the database. If, however, the guiLabel item exists and an attribute is defined in that item with the same name as a column label, then the corresponding value will be used as the column label when the table is retrieved. For example, if the following items are defined:
item guiLabel { UserName = "User Name"; Street = "Street Address"; CityState = "City, State"; } item getData extends SQLTableAction { sqlFile = "getUserData.sql"; }
then the labels defined in the guiLabel item will be assigned to each of the corresponding columns in the table returned by the getData action item. In most cases, it is beneficial to define a single guiLabel item containing all of the labels used for a given application and then to include that item in all transactions. In fact, the skipjack-labels.tdf already defines the labels used by the internal Skipjack transactions and that file is automatically included whenever the skipjack-common.tdf file is included. Consequently, if you want to add or modify labels to the guiLabel you need only overwrite the default guiLabel item as follows:
include "skipjack-common.tdf" item guiLabel overrides guiLabel { _readonly = true; UserName = "User Name"; Street = "Street Address"; CityState = "City, State"; }
Note, the "overrides" directive is used instead of the common "extends" directive because both items have the same name and the overrides directive tells the compiler that you really want to re-use the name. You can also use the "replaces" directive, which effectively erases all attributes defined by the original guiLabel item, using only the attributes defined in this item definition. You should also note the use of the "_readonly" attribute, which tells the server that the guiLabel item defined here will not be updated during transaction processing. This information allows the server to optimize the internal processing of items, decreasing the overall time needed to initialize a transaction. See Performance Tuning for more details.
5/11/2007 10:14 AM
42 of 150
http://205.177.219.178/doc/appDevGuide/printable.html
The sqlErrorMap defines a list of tuples, where each entry specifies how a matching set of exceptions should be handled. When a database exception is generated, the resulting error message is compared against the GNU regular expressions defined as the first element of each tuple. If the message matches one of the expressions (processed from top to bottom) then the second element in that tuple is assigned to the tx.status attribute and the third element in the tuple is assigned to the tx.errorMsg attribute. At that point, the action is aborted, which causes the transaction to be aborted and the tx.status and tx.errorMsg are sent back to the user or client application as part of the failed transaction's response. In general, it is not necessary to specify the sqlErrorMap for most SQL actions, since the database error message is probably sufficient to explain the source of the problem. However, in some cases, it is desirable to hide the details from the user, supplying a more application-specific error message. (See Error Response for more details.)
43 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
44 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
that retrieves the list of databases from the database server, referencing the following response template file (ex1.vm):
## ## ex1.vm ## <body bgcolor="white"> <h2>Database List</h2> <pre> #foreach ($row in $getDBList.rows) $row.getValue(0) #end </pre>
In this case, the response template is written as an HTML file, which displays the list of databases using standard HTML tags. For most transactions, this is all that is needed and the template filename can simply by specified using the main.templateName attribute.
45 of 150
http://205.177.219.178/doc/appDevGuide/printable.html
The COMMON/tx-success.vm template allows a transaction author to easily generate output for transactions that only need to display a simple message and then branch to another URL when the user acknowledges the message.
Success URL
If the main.successURL or input.successURL attributes are defined, then it will be used as the URL invoked by the "OK" button that is displayed beneath the message. That is, when a browser user presses that button to acknowledge the message, the browser will go to that URL as the next step in the user interaction. To facilitate sending input parameters to the successURL, all of the input attributes defined when the transaction response is generated are defined as hidden HTML form parameters, which will cause them to be submitted when the successURL is invoked by the "OK" button. If no main.successURL attribute is defined, the "OK" button will function just like the "Back" button provided by the browser. For example, if we modify the previous transaction as follows (ex2.tdf):
// // ex2.tdf // include "skipjack-common.tdf" item getDBList extends SQLTableAction { sqlFile = "../dbQuery/getDBList.sql"; } item main extends BaseAction { actions = ["getDBList"]; successTitle = "Database List Retrieval"; successMsg = "Found "+getDBList.rowCount+" Databases"; templateName = "/COMMON/tx-success.vm"; }
then it will display the following HTML output when executed from within a browser:
46 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
then the /test/showInput.tdf transaction will be executed when the "OK" button is pressed, displaying any input parameters from the ex3.tdf transaction passed as hidden parameters to the showInput.tdf transaction. For example, if you enter the following URL in a browser:
http://$SKIPJACK_URL/tutorial/txResponse/ex3.tdf?name=Fred&salary=123
then the "name" and "salary" input attributes will be displayed (along with some internal attributes) when the "OK" button is pressed. The tx-success.vm template uses the input.successURL attribute value if the main.successURL is not defined. Therefore, it is possible for a high-level transaction to specify the URL (as a hidden form field, for example) that should be used by a lower-level transaction. In this way, the low-level transaction can be called from different high-level transactions and can automatically branch to different destinations when the user acknowledges the message.
Success Redirect
If the main.successRedirect or input.successRedirect attributes are defined as "true", then the HTML output generated by the tx-success.vm template will automatically cause the client browser to be redirected to the success URL. In such cases, the success message and button will not be displayed, but all hidden input fields will be sent to the success URL as if the user had pressed the button themselves. Such a mechanism is usually used when executing a low-level transaction to perform a server-side function, such as deleting a record in the database, and then re-executing the high-level transaction, such as a list of all records in the database, without requiring the user to acknowledge the successful outcome. For example, if the following URL is entered:
47 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
http://$SKIPJACK_URL/tutorial/txResponse/ex3.tdf?name=Fred&salary=123&successRedirect=true
then after the ex3.tdf transaction is executed, the browser will immediately proceed to the ex2.tdf transaction which will display all the input parameters that were copied from the original transaction URL.
Response Map
The main.templateName attribute is actually the default value used by the transaction's "response map" which is used to determine the specific template to be used based on the desired MIME type. The response map is defined by the main.responseMap attribute, which uses the following default setting for all transactions:
responseMap = ("transaction/map", (".*", TransMapResponse), templateName);
templateName = "/COMMON/tx-status.vm";
The main.responseMap is processed from top to bottom, using the first entry in each tuple as a GNU regular expression that is compared to the
response.contentType
attribute. If a match is found, the second entry in the tuple indicates either the name of a Velocity file to be processed or the name of an action to be performed. The response.contentType can be defined statically in the TDF file by the transaction author or can be defined dynamically using attribute expressions or actions. Furthermore, if the input.contentType is defined when the transaction is executed, it will be used to overwrite the
response.contentType
attribute. This allows users and client applications to specify the desired output content type. If no value is specified, a default value of "text/html" is used. As can be seen in the default definition above, if the transaction/map MIME type is set, then the TransMapResponse action will be executed, which generates a data output stream that encodes the attribute values for the input, output and tx items. (See Client Application Support for more details about processing transaction output using the transaction/map MIME type.) Otherwise, the wildcard expression .* will match and the template file name (or action name) specified by the templateName attribute will be processed. This provides a convenient mechanism for transaction authors to specify the most common response mapping (a single template), while allowing them to override the responseMap itself for more involved situations. For example, if we modify the previous example as follows (ex4.tdf):
item main extends BaseAction { actions = ["getDBList"]; successTitle = "Database List Retrieval"; successMsg = "Found "+getDBList.rowCount+" Databases"; successURL = "/test/showInput.tdf";
48 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
then the same HTML success message will be generated by default using the /COMMON/tx-success.vm template. However, if the following URL is executed:
http://$SKIPJACK_URL/tutorial/txResponse/ex4.tdf?contentType=text/plain
then plain text output will be generated using the /COMMON/tx-status.vm template file.
Error Response
When an exception is generated by a transaction, all action processing is immediately aborted and a transaction response is generated based on the value of the response.contentType attribute. Such processing is controlled by the main.errorMap attribute, which is defined as follows by default:
errorMap = (".*ResourceNotFoundException", guiStatus.ResourceNotFound, "/COMMON/NotFoundException.vm"), (".*SQLException", guiStatus.DatabaseException, "/COMMON/SqlException.vm"), (".*UserException", guiStatus.UserException, "/COMMON/UserException.vm"), (".*SecurityException", guiStatus.SecurityException, "/COMMON/SecurityException.vm"), (".*", errorStatus, "/COMMON/UnknownException.vm"); errorStatus = guiStatus.UnknownException + " - " + tx.errorClass;
Much like the main.responseMap attribute, the main.errorMap is processed from top to bottom, looking for a match using the first entry in each tuple as a GNU regular expression. However, unlike the responseMap the regular expression is compared with the Exception Class as designated by the
tx.errorClass
attribute, which is automatically defined when the exception is generated. When a match is found, the second entry in the tuple is assigned to the tx.status attribute and the third entry is used to generate the transaction response, which may consist of an action name or Velocity template file. The main.errorMap attribute is defined for all actions in the COMMON/skipjack-common.tdf file, referencing a set of default HTML template files for various
49 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
common exceptions. Transaction authors can override that definition to specify custom error handling information. Typically, even when custom error maps are defined, it is prudent to assign a standard Skipjack or application status value. The standard Skipjack status values are defined by the guiStatus which is specified in the COMMON/skipjack-status.tdf file. Transaction authors are free to override or extend the guiStatus item as needed.
Response Actions
A transaction response can be generated using either a Velocity template file, a generic response action or a custom response action. In all cases, response processing occurs after all other actions referenced by the main action item are processed. The following response actions are provided by the Skipjack Server: FileResponse Retrieves a given file from the server's file system and uses it as the transaction response. See Shell Script Processing for more details and an example of how it can be used. ShellResponse Executes an operating system/application shell script/executable and uses the output of that script as the response for the current transaction. See Shell Script Processing for more details. TxResponse Executes a sub-transaction on any Skipjack Server, local or remote, and uses the response from that transaction as the response of the current transaction. See TxResponse Class for more details. TransMapResponse Generates a data stream by encoding all of the attribute values defined by one or more transaction items using an encoding scheme defined by the navius.toolkit.format.TransactionMap Java class. See TransMapResponse Class for more details. TransPropResponse Generates a data stream by encoding all of the attribute values defined by one or more transaction items as a Java properties file. See TransPropResponse Class for more details. In all cases, the response action is responsible for setting an appropriate value for the Content-Type HTTP header that is sent back to the client application. In some cases, such as the TransMapResponse response action, the content type is statically defined, while in other cases, such as the FileResponse and ShellResponse response actions, the transaction author can specify the proper type using the mimeType attribute.
50 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
ShellResponse Action
In many cases, the output generated by a shell script can be directly used as the output of the transaction and the shell script input can be fully specified using command line options. In such cases, the ShellResponse response action can be used to execute the script and use the output directly as the transaction response. For example, the following transaction (test/sysConfig.tdf):
item genResponse extends ShellResponse { shellCmd = skipjack.BINDIR + "SystemConfig.pl"; mimeType = "text/plain"; } item main extends NOP { templateName = "genResponse"; }
executes the standard SystemConfig.pl script that is provided by the Skipjack Server, using the resulting output as a plain text transaction response. In this case, the templateName attribute references an action, not a Velocity template filename, as is typical. The transaction engine automatically detects this situation and it uses the specified action to generate the transaction response, instead of using the Velocity template engine.
51 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
In this example, the main action extends the NOP action because it doesn't need to perform any actions other than the genResponse action, which is automatically executed after the main action is done (doing nothing). The ShellResponse response action provides a simple and effective mechanism for integrating the Skipjack Server with existing scripts or executables that generate an output stream that does not require additional processing.
ShellAction
The ShellAction allows a transaction author to execute a given shell command and then continue transaction processing. If an optional input file is defined, then that file will be processed using Velocity and sent to the standard input stream of the shell command when it is executed. This allows a transaction to dynamically construct shell input scripts or control files. Any output required from the shell command can be saved directly to a file by the shell script, saved to a file by the transaction, appended to a server log file or processed directly by the transaction itself. For example, the following transaction definition provides a way to restart the Skipjack Server:
// // restart.tdf // include "skipjack-common.tdf" item main extends ShellAction { shellCmd = "/tools/skipjack/bin/skipjack.sh restart"; }
In this case, the shell command is fully specified and there is no need to define the inputFile attribute because the shell script doesn't accept any input. Note that no templateName attribute is defined in the main item. In this case, the default template file /COMMON/tx-status.vm is used, which simply displays the transaction status when it has completed. (In this specific case, however, the transaction will never finish because the server will be restarted by the shell script.)
52 of 150
http://205.177.219.178/doc/appDevGuide/printable.html
// // restart.tdf // include "skipjack-common.tdf" item main extends ShellAction { shellCmd = skipjack.BINDIR + "skipjack.sh restart"; }
Note, the paths stored in the skipjack attributes already include a trailing path delimiter. The test/showRequest.tdf transaction can be executed to display a list of all skipjack attributes defined by the server, as well as all request attributes that are defined as the result of the transaction request being processed.
The "-" command line argument to the gnuplot utility instructs it to accept a stream of plotting commands from its standard input stream. Since the inputFile attribute is defined, the "chart.vm" file:
set output '$curAction.chartFile' set terminal png small color set xrange [0:10] set yrange [0:10] set xlabel "X Axis" set ylabel "Y Axis" set title "Test Chart" set size 1.0,0.8 plot '-' title "" with lines 0 0 1 1 2 2 3 3 4 4
53 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
5 5 6 4 7 3 8 2 9 1 10 0
will be processed by Velocity and the resulting output will be sent to the gnuplot command. Although the input file may look a little cryptic, anyone familiar with gnuplot could easily write such a script. Note how the $curAction.chartFile attribute is referenced in the input file to specify the output file to be generated by gnuplot. Furthermore, by examining the item definition above, we see that the chartFile attribute is constructed from a temporary path prefix and the processID attribute, which is automatically defined to be a unique value whenever a ShellAction is performed. In this way, we can ensure that concurrent executions of the genChart action don't interfere with each other.
The genResponse action extends the FileResponse action, specifying the file to be used as the transaction response as the outputFile attribute. Note the reference to genChart.chartFile which ensures that the genResponse action references the same output file generated by the genChart action. The mimeType
54 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
attribute is also used to define the MIME type sent to the user or client application as part of the HTTP response. Finally, the deleteFile attribute is used to remove the output file when the action is done. Since the purpose of the FileResponse action is to generate the transaction response, we need to reference it from the main.templateName attribute (or override the main.responseMap attribute). See Transaction Response for details about generating a custom transaction response.
55 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
In addition to the environment variables listed above, all of the transaction attributes defined for the "input", "request", "tx", and "skipjack" items will be defined as environment variables, using the format "item_attr" for the variable names. For example, the "skipjack.ROOTDIR" transaction attribute can be accessed by the shell script as the "skipjack_ROOTDIR" environment variable and the "input.X" transaction attribute as the "input_X" environment variable. Because of operating system environment variable name restrictions, all '-' (dash) characters in the original transaction attribute name will be replaced by '_' (underscore) characters. For example, the "request.Accept-Encoding" transaction attribute will be defined as the "request_Accept_Encoding" environment variable.
56 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
values of "705 Shell Action Failed" and "Shell Exit Value = xxx", respectively. For example, the following action specifies a custom error message, depending on the _shellStatus value:
item doScript extends ShellAction { shellDir = "/my/working/directory"; shellCmd = "myScript -x 12 -y 23"; exceptionOnError = true; exceptionMsg } = _shellStatus > 0 ? "Bad Shell Status = "+_shellStatus : "Really Bad Shell Status = "+_shellStatus;
The exceptionOnError attribute supported by the ShellAction should not be confused with the _ignoreError attribute that is supported for all actions. The _ignoreError attribute causes the action processing engine to ignore any exception raised by a given action, thereby allowing the transaction to continue. The exceptionOnError attribute determines if an exception should be raised because of a non-zero shell exit status, it does not prevent other types of exceptions from being thrown.
In this case, an exception is raised only if the status is less than zero. Clearly, more complex evaluation criteria can be used and more advanced failure recovery is possible, based solely on the exit status.
57 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
outputSeverity If outputDest is 'LOG', this specifies the severity of the log record that is generated (default = DEBUG) outputPrefix outputAction If outputDest is 'LOG', this specifies the prefix for each log record that is generated (default = action name) If outputDest is 'ACTION', this specifies the name of an action (or a vector of action names) which should be executed just before the shell command, just after completion of the shell command, and once for each output line. The following attributes are defined in the destination item before each action is executed:
_curStep
- one of (PRE, POST, LINE) where 'PRE' indicates that the shell command is about to be run; 'POST' indicates that the shell command has just completed; 'LINE' indicates that an output line was generated - the current output line - the shell exit status (only valid when _curStep = POST)
_curLine _shellStatus
errorDest
58 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Same functionality as outputFile for STDERR output lines Same functionality as outputSeverity for STDERR output lines (default = ERROR) Same functionality as outputPrefix for STDERR output lines Same functionality as outputAction for STDERR output lines
Simply by specifying the errorDest attribute as "LOG", all lines printed to the shell script's STDERR output stream will be appended to the Skipjack Server error log (the default errorFile value). By default, the log entries will be assigned an errorSeverity of 'ERROR' and an errorPrefix of 'myTrans.doScript', where 'myTrans' is the name of the current transaction.
In this case, the STDERR output lines are appended to the Skipjack Server error log, while the STDOUT output lines are written to the "/tmp/myScript.out" file.
59 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Keep in mind that multiple transactions can be executing concurrently, which usually requires dynamically generated unique filenames to keep different transactions from using the same file. Review the chart example above to see how the processID can be used in such cases.
outputDest = "ACTION"; outputAction = ["deleteRecord"]; } item genAlarm extends TxAction { preCondition = doScript._curStep == "LINE"; _transName Severity Message } item deleteRecord extends SQLTableAction { preCondition = doScript._curStep == "LINE"; sqlFile ID } = "deleteMyRecord.sql"; = doScript._curLine; = "alarmMgr/insert.tdf"; = "ALARM"; = doScript._curLine;
In this example, an operator alarm is created for every STDERR output line by the genAlarm action. The genAlarm action uses the preCondition attribute to indicate that it should only be executed when the doScript._curStep attribute is "LINE", not for the "PRE" or "POST" action processing steps. It then uses the output line generated by the shell script as the alarm message. The deleteRecord action is used to process each line generated on STDOUT by the shell script. In this example, each line consists of a single database record identifier, which is simply passed to the "deleteMyRecord.sql" database script. Here again, the deleteRecord action specifies a preCondition attribute so
60 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
that it is not executed for the "PRE" and "POST" steps. Clearly, by defining additional processing actions most output processing tasks can be performed in this way. If not, the output information can always be added to a table by the processing actions, for subsequent processing by other transaction actions executed after ShellAction processing is complete.
61 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
the associated process registration. In such cases, the shell exit status will be set to "666" to signify that the shell script was aborted by the Process Manager. All ShellAction actions, regardless of the background attribute setting, have at least one process registered with the Process Manager. This means that even a "foreground" shell script execution can be aborted using the Process Manager. In that case, an exception will be generated by the ShellAction and the transaction can react accordingly.
62 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
63 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
64 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Generally, these attributes serve as convenient ways to specify a collection of actions, transactions or users to be monitor for a group of transactions, in which case the 'debug' item may be defined in a common file and included into all transactions in the group.
65 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
transaction can be used to display the current lists of users, transactions and actions being monitored, and to add or remove entries from each list. Whenever the lists are updated in this way, the server will automatically start monitoring transactions accordingly. During the execution of a given transaction, if the current username is contained in the user list or the current transaction identifier is contained in the transaction list, then all debug information associated with the transaction will be generated in the trans.log and action.log files. If no user or transaction match is found, then the settings within the transaction definition file will be used to control debug generation. During the execution of a given action within a given transaction, if the current action name is contained in the action list, then the action debug information will be generated in the action.log file. The action name entered in the DebugControl action list should always be prefixed by the name of the transaction. For example, if you wanted to monitor the 'genChart' action defined by the 'test/chart' transaction, then you should add an entry for 'test/chart.genChart' to the action list.
LogAction
The LogAction can be used by a transaction to append a log message to any of the log files supported by the Skipjack Server. The log message itself is generated by concatenating the values assigned to the following action item attributes: GroupID ElementID EventTraceID ErrorCode Message which are processed as strings. The exact meaning and usage of each field is application-specific and if a given field is not defined, it will be ignored. If the LogFile attribute is defined, then it will be interpreted as the Log4j Category name for the log file to be appended. The default value is "navius.skipjack.errorlog", which is the Category associated with the $SKIPJACK_HOME/logs/error.log log file. See the $SKIPJACK_HOME/app/WEB-INF/conf/log.cfg configuration file for a list of all Category names supported by the server. If the Severity attribute is defined using one of the following values: ERROR
66 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
WARNING INFO DEBUG then it will be used as the severity level of the resulting message when it is written to the log file. If the severity is lower than the filter level currently set for that log file, the message will be ignored. The default severity level is "INFO".
67 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
ensures that the ID attribute is between 1 and 99, and that the Color attribute is either "red", "white", or "blue" before the database action is executed. The preErrorCheck attribute can consist of one or more tuples, with the first entry in each tuple used as the error criteria. That is, if the first entry evaluates to "true", then an error exists. In that case, the second entry is assigned as the value of tx.status attribute and the third entry is assigned as the value of the
68 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
tx.errorMsg
attribute. Error criteria are evaluated from top to bottom until one of the criteria is "true" or until the bottom of the list is reached.
ensures that at least one row is returned by the database action, by checking the value of the rowCount attribute that is automatically defined by the SQLTableAction action after the SQL query is processed. As with the preErrorCheck attribute, multiple error criteria can be defined, accessing local attributes or attributes defined in other items to determine if transaction processing should be aborted.
69 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
User Authentication
By default, all resources and transactions provided by Skipjack require the client or user to provide authentication information for every HTTP or HTTPS request. When connecting to Skipjack via a browser, the information is acquired by the browser using either a standard login pop-up window or by the server application using a custom login screen (see Custom Login Screens for details). When connecting to Skipjack using the C++, Java or Perl Client Library, the user authentication information is provided using the setAuthInfo() method defined by the Transaction class. (See Client Application Support for more details). Regardless of how the authentication information is provided, it is used by Skipjack for two security purposes. First, the username and password provided are validated against the Access Control List (ACL) maintained by the server. If the username or password are invalid, access is denied and an error response is generated accordingly. If the username and password are valid and the user is attempting to access a static file (such as a GIF or HTML file) then no further access control is performed and the resource is returned to the client. If the client is attempting to execute a transaction, then Skipjack retrieves the list of one or more roles that has been assigned to that user (the User Role List) and the list of one or more roles that are required by the transaction (the Tx Role List). If the User Role List and the Tx Role List intersect, then transaction execution is allowed to proceed. If not, an appropriate error message is generated.
Transaction Authorization
Transaction authors can assign one or more required roles to a transaction simply by defining the security.roles action item in the transaction definition file. For example, the following definition:
item security { roles = ["Operator", "Supervisor"]; }
70 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
requires that the user be assigned the "Operator" or "Supervisor" role in order to execute the current transaction. If the security.roles attribute is not defined, then no security check is performed and every user is allowed to execute the transaction.
Guest Resources
In many cases, access to certain files and/or transactions need not be controlled and shouldn't require the user or client to provide a valid username or password. Such resources are called "Guest Resources" and are supported in Skipjack using a set of wildcard definitions. If a requested file or transaction path matches one of the wildcard expressions (defined as a GNU regular expression), then access will be granted to the user, regardless of the validity of the username and password supplied, if any. By default, the user will be granted the "_GUEST" username and the "_Guest" role during such accesses, but operators and developers may assign other usernames with different roles as needed.
71 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
ACLAction Processing
The ACL memory tables are not intended to be accessed directly by transactions. Instead, the ACLAction action item may be used to retrieve data from the ACL memory tables. The ACLAction performs a specific function based on the setting of the cmd attribute, with the following valid values: getRoleList getUserList getUserRoles getUserTable getGuestTable validateRole reloadACL In most cases, the requested data is stored in an attribute with an appropriate name. See ACLAction Class for more details. The "reloadACL" command is used to reload the ACL memory tables from the corresponding ACL database tables as defined by the tableACL.cfg configuration file. This command is executed by the User Admin Tool whenever the ACL database tables are updated.
ACL Configuration
The Skipjack ACL is configured using the security.cfg and tableACL.cfg configuration files found in the $SKIPJACK/app/WEB-INF/conf directory. The security.cfg file defines the names of the ACL tables, while the tableACL.cfg file defines the standard table definitions for the ACL Memory Tables listed above. Application developers may overwrite one or both of these files to install custom ACL tables. For example, an application may overwrite the security.cfg file provided by Skipjack and specify different names for the tables to be used from another table definition file. (See Memory Table Processing for more details.) Alternately, an application may only overwrite the tableACL.cfg file, to define a different set of source database tables or delimited data files, while still using the same names defined in security.cfg. This is most commonly done when the ACL tables will be retrieved from a database other then the standard "skipjack" database. (See Database Processing for more details about using connections to multiple database servers.) Finally, applications can simply append their own default authentication information to the standard Skipjack ACL tables when the application is initially installed.
72 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Applications should refrain from automatically reloading the ACL tables if they already exist, since the operators may have modified their contents and probably don't want to type it all back in again! To facilitate the sharing of the ACL tables between applications, each of the ACL tables has an 'AppName' column that may be used to identify the application that loaded that entry. For example, all of the default Skipjack entries are marked with an AppName of 'SKIPJACK'. Installation scripts can make use of the AppName column to determine how existing data should be handled.
ACLLogin Table
Forms-based authentication is configured in Skipjack by inserting one or more records into the ACLLogin database table, which is normally done at installation time. The Skipjack Server defines the following default record: Resource LoginURL AppName
/test/formLogin/.* /skipjack/login-form.tdf SKIPJACK which specifies that the /skipjack/login-form.tdf transaction should be executed whenever login information is needed to execute any file in the /test/formLogin/ directory. Application developers may add one or more additional records to define the login transactions (or static files) to be used for other individual transactions or groups of transactions. For example, the following definition:
73 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Resource
LoginURL
AppName
would cause the /myapp/login.tdf transaction to be executed for all access attempts in the /myapp/ directory. Application installation scripts may use the AppName column in the ACLLogin table to determine which records can be overwritten during re-installation without adversely affecting the settings required by the Skipjack Server or another application.
ACLGuest Table
For every LoginURL defined in the ACLLogin, a corresponding entry should also be made in the ACLGuest table to indicate that any user can access that resource and to specify the internal username associated with that resource. In normal applications, the _GUEST username can be used for this purpose. For example, the following entry in the ACLGuest table Resource UserName AppName MYAPP
/myapp/login.tdf _GUEST
allows any user to access the login form transaction specified by the previous example.
74 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
session.skipjackOrigURL
session.skipjackLoginURL login servlet URL (ie. "/skipjack/login") The skipjackOrigURL attribute will include a query string that defines all of the input parameters that were provided (using either the GET or POST methods) for the URL that was originally requested. Login form transaction authors can use these attributes in any way that they want to display the login form, as well as any other attribute normally available to a transaction. (See Session Management for more details about the session item and related session processing features.) The default login form is defined by the "/skipjack/login-form.tdf" transaction and the "/skipjack/login-form.vm" and "/skipjack/login-page.vm" templates. If you want to make a custom login form, you can use those files as examples to build your own or can include those files and override attributes as needed to build your own login forms and/or pages. In normal practice, there is no need to define the security.roles attribute for a login form transaction, because it should be accessible to everyone in order to allow them to log into the server.
See the /skipjack/login-form.vm and /skipjack/login-page.vm templates for an example of a more useful login form and page layout.
75 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
will execute the test/nop.tdf transaction and return the result as a text/html file, which is the default response MIME type for any transaction. The Authorization header shown above is the encoding for a username and password of "admin/admin". If a different login is required, a different Authorization header should be used.
76 of 150
http://205.177.219.178/doc/appDevGuide/printable.html
parameters. To that end, the transaction/map MIME is supported by the Skipjack Server for all transactions. The transaction/map file format consists of a small number of formatted record types that can be used to encode the following types of data: Scalar Value An arbitrary string that represents a single value, representing strings, numbers, dates, etc. List Value An ordered list of scalar values, where each string may represent a different type of value. Table Value A two dimensional set of data represented as rows and columns. Each column has an associated name and default value and each row contains a value, possibly empty, for each column defined. Map Value A hashtable of key/value pairs. Although the number of record types is small, they are sufficient to transfer all of the output values that can be generated by standard Skipjack transactions. The transaction/map format is designed to be easily processed by client applications and/or read directly by humans for debugging purposes. Client applications that require different encoding schemes can be easily supported by creating a Velocity template that encodes the necessary transaction results as needed and then assigning that template to a custom MIME type in the responseMap for one or more transactions. See Transaction Response for more details. A transaction/map can be generated for any transaction simply by specifying that MIME type as the value of the contentType input parameter. For example, the following raw HTTP request:
GET /test/nop.tdf?contentType=transaction/map HTTP/1.0 Host: localhost:8080 Authorization: Basic YWRtaW46YWRtaW4=
executes the test/nop.tdf transaction, setting the contentType to transaction/map, causing the following server response to be generated:
K|input.contentType V|transaction%2Fmap K|tx.statusHistory L|0 K|tx.stopTime V|1019597119992 K|tx.startTime V|1019597119966
77 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
K|tx.respTime V|26 K|tx.status V|100+Transaction+Successful K|tx.clientID V|UNKNOWN K|tx.errorMsg V| K|tx.TXDIR V|%2Fhome%2Ftools%2Fskipjack%2Fapp%2FWEB-INF%2Ftransaction%2Ftest%2F K|tx.errorClass V| K|tx.identifier V|test%2Fnop K|tx.errorTrace V| K|tx.basename V|nop K|tx.actionTrace L|2 V|main V|TransMapResponse
See the navius.toolkit.format.TransactionMap class for details concerning the encoding scheme.
78 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
when accessing the server. See Perl Client Package for more details. C++ Client Package Client applications developed using C++ can use the Skipjack C++ Client Package to access the Skipjack Server. The C++ Client Package provides complete control over the execution of transactions and the processing of results, using the HTTP protocol to access the server. See C++ Client Package for more details.
79 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
Specifies the authentication information (in the form "username:password") to be used when submitting each transaction for execution. By default, no authentication information is provided.
public void setClientID (String clientID)
Specifies the client ID for this application, which is used in the Skipjack Server logs to determine the client that executed a given transaction. By default, a client ID based on the current process ID and the hostname is used.
public void setServerURL (String serverURL)
Specifies the base URL to be used when a relative URL is provided as an input parameter to the execute() method. By default, no server URL is defined.
public void debugEnable (boolean enabled)
80 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Enables/disables debug output generation for the Transaction object. Disabled by default.
Input Parameters
Returns a hash table containing all of the input parameters that are currently defined
81 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Transaction Processing
Executes the given transaction and processes all of the results returned. If a relative URL is given, then it will be prepended by the current base server URL as specified by the setServerURL() method. This method automatically clears all existing transaction results before executing the new transaction. After this method is called, the tx.status result will contain the transaction status, which should be checked by the client application before accessing any other results. If a transaction/map response is returned by the Skipjack Server, then all of the results will be automatically stored in the result map. Otherwise, the entire file returned by the Skipjack Server will be stored as a string in the respData result.
Results Processing
Returns a hash table containing all of the results that are currently defined.
Additional methods are supported for more advanced processing, as described in the Java API documentation for the Transaction class.
82 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
tx.execute ("http://localhost:8080/test/nop.tdf"); int status = tx.getResultInt("tx.status"); if (status < 100 || status > 199) throw new Exception ( tx.getResultString("tx.status") + " - " + tx.getResultString("tx.errorMsg"));
In this case, the test/nop.tdf transaction is executed, using "admin:admin" to authenticate this client application. After the transaction is executed, the
tx.status
result is examined as an integer to determine if the transaction was successful. (By convention, status values between 100 and 199 inclusive are considered successful.) If not, the tx.status and tx.errorMsg results are used as strings to generate an exception message.
executes the test/ping.tdf which accepts the message input parameter, specified using the setParam() method, and returns the output.message result, which is accessed using the getResultString() method. In this example, the setClientID() method is used to specify a string that can be used to identify this client application in the server logs. If not specifically defined, a default client ID is generated based on the current process ID and host name.
Processing Results
5/11/2007 10:14 AM
83 of 150
http://205.177.219.178/doc/appDevGuide/printable.html
In the following example, the sqlUtil/adhocSql transaction is executed to retrieve a table containing the names of all databases currently defined:
import navius.skipjack.client.*; import navius.skipjack.table.*; Transaction tx = new Transaction(); tx.setAuthInfo ("admin:admin"); tx.setClientID ("myClient"); tx.setParam ("sqlExpr", "show databases"); tx.execute ("http://localhost:8080/sqlUtil/adhocSql.tdf"); int status = tx.getResultInt("tx.status"); if (status < 100 || status > 199) throw new Exception ( tx.getResultString("tx.status") + " - " + tx.getResultString("tx.errorMsg")); Table tbl = (Table)tx.getResultItem("output.table"); for (int i=0; i<tbl.size(); i++) System.out.println (tbl.getValue(i,"Database"));
In this case, the sqlExpr input parameter is defined, as required by the sqlUtil/adhocSql transaction and the output.table result is accessed. Since database queries can return multiple rows and columns, the result is accessed using the getResultItem() method, which returns the object that is actually stored in the result map. In this case, it is known to be a Table object, so that result is cast accordingly and each value in the "Database" column of that table is printed.
84 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
In this case, the text/html content type is specified, which causes the entire output file generated by the server to be stored as the "respData" result. Note, the tx.status value can still be accessed to determine if the transaction was successful, but no other transaction output parameters will be available.
85 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
Specifies the root directory for the Skipjack Server or Skipjack Client Package where the wget executable can be found. Default is "/tools/skipjack".
$tx->setAuthInfo (authinfo)
86 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Specifies the authentication information (in the form "username:password") to be used when submitting each transaction for execution. By default, no authentication information is provided.
$tx->setClientID (clientID)
Specifies the client ID for this application, which is used in the Skipjack Server logs to determine the client that executed a given transaction. By default, a client ID based on the current process ID and the hostname is used.
$tx->setServerURL (url)
Specifies the base URL to be used when a relative URL is provided as an input parameter to the execute() method. By default, "http://localhost:8080" is used.
$tx->debugEnable (enabled)
Enables/disables debug output generation for the Transaction object. Disabled by default.
87 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Input Parameters
Returns an associative array containing all of the input parameters that are currently defined
Transaction Processing
$tx->execute (url)
Executes the given transaction and processes all of the results returned. If a relative URL is given, then it will be prepended by the current base server URL as specified by the setServerURL() method. This method automatically clears all existing transaction results before executing the new transaction. After this method is called, the tx.status result will contain the transaction status, which should be checked by the client application before accessing any other results. If a transaction/map response is returned by the Skipjack Server, then all of the results will be automatically stored in the result map. Otherwise, the entire file returned by the Skipjack Server will be stored as a string in the respData result.
Results Processing
Sets the type and value of the given result. This method is called automatically by the execute() method whenever a transaction/map response is returned by the Skipjack Server.
$tx->getResult (key)
Returns the data type of the given result (V=value, L=list, T=table). The result type can be used to determine how the result should be processed by the client.
88 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
$tx->getResultMap ()
Returns an associative array containing all of the results that are currently defined.
$tx->printAllResults (FILE)
Client Status
Sends the given message to the Skipjack Server log based on the given severity (ERROR, WARNING, INFO, DEBUG)
$tx->alarm (severity, gid, eid, tid, code, msg)
Generates an alarm or alert on the Skipjack Server with the given information. The severity may be ALARM or ALERT.
89 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
In this case, the test/nop.tdf transaction is executed, using "admin:admin" to authenticate this client application. After the transaction is executed, the tx.status result is examined to determine if the transaction was successful. If not, the tx.status and tx.errorMsg results are used to generate an error message. In this example, the $SKIPJACK_ROOT variable is used to specify the location of the Transaction.pm module in the Perl require statement (which loads the named module) and the setSkipjackRoot() method (which specifies the location of the wget executable). The pathname specified for $SKIPJACK_ROOT should be either the Skipjack Server root directory or the Skipjack Client root directory, depending on which packages are installed on the same machine as the Perl client application. In both cases, the Transaction.pm module is located in the lib subdirectory and the wget executable is located in the bin subdirectory.
90 of 150
http://205.177.219.178/doc/appDevGuide/printable.html
the Skipjack Server URL is used as the default serverURL for all transactions that are specified using relative transaction names, and the authentication information for the user who executed the transaction that executed the current script is used as the default authentication information for any transactions executed by this script. As a result of these conveniences, if the previous script example were executed directly by the Skipjack Server it could be written as follows:
# # PerlClient-ex2.txt # require "Transaction.pm"; $tx = new Transaction(); $tx->execute ("test/nop.tdf"); $status = $tx->getResult("tx.status"); if ($status ne "100 Transaction Successful") { print "STATUS: $status\n"; print "ERROR: ",$tx->getResult("tx.errorMsg"),"\n"; exit -1; }
Note how the require statement and the execute() method make use of relative names, and that no other configuration methods need to be called before executing the transaction.
91 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
if ($status ne "100 Transaction Successful") { print "STATUS: $status\n"; print "ERROR: ",$tx->getResult("tx.errorMsg"),"\n"; exit -1; } print "message = ",$tx->getResult("output.message"),"\n";
executes the test/ping.tdf which accepts the message input parameter, specified using the setParam() method, and returns the output.message result, which is accessed using the getResult() method. In this example, the setClientID() method is used to specify a string that can be used to identify this client application in the server logs. If not specifically defined, a default client ID is generated based on the current process ID and host name.
Processing Results
In the following example, the sqlUtil/adhocSql transaction is executed to retrieve a table containing the names of all databases currently defined:
# # PerlClient-ex4.txt # $SKIPJACK_ROOT = "/tools/skipjack"; require "$SKIPJACK_ROOT/lib/Transaction.pm"; $tx = new Transaction(); $tx->setSkipjackRoot ($SKIPJACK_ROOT); $tx->setAuthInfo ("admin:admin"); $tx->setClientID ("myClient"); $tx->setParam ("sqlExpr", "show databases"); $tx->execute ("http://localhost:8080/sqlUtil/adhocSql.tdf"); $status = $tx->getResult("tx.status"); if ($status ne "100 Transaction Successful") { print "STATUS: $status\n"; print "ERROR: ",$tx->getResult("tx.errorMsg"),"\n"; exit -1; } print "Database List\n"; print "-------------\n"; @rows = @{$tx->getResult("output.table")}; foreach $row (@rows) { %row = %{$row}; print $row{"Database"},"\n"; }
In this case, the sqlExpr input parameter is defined, as required by the sqlUtil/adhocSql transaction and the output.table result is accessed. Since database queries can return a table of results, the result is referenced as an array of table rows. Each table row is implemented as an associative array, using
5/11/2007 10:14 AM
92 of 150
http://205.177.219.178/doc/appDevGuide/printable.html
the name of each table column as the key. The last loop in the example iterates over all rows, printing the value of the "Database" column, which is a known result column for the given query. The printAllResults() and printResult() methods can be used to print all currently defined results or a given result for debug purposes.
In this case, the text/html content type is specified, which causes the entire output file generated by the server to be stored as the "respData" result. Note, the tx.status value can still be accessed to determine if the transaction was successful, but no other transaction output parameters will be available.
93 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
Specifies the authentication information (in the form "username:password") to be used when submitting each transaction for execution. By default, no authentication information is provided.
void setClientID (const char *clientID)
Specifies the client ID for this application, which is used in the Skipjack Server logs to determine the client that executed a given transaction. By default, a client ID based on the current process ID and the hostname is used.
void debugEnable (int enabled)
Enables/disables debug output generation for the Transaction object. Disabled by default.
Input Parameters
94 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Returns a TransactionMap object containing all of the input parameters that are currently defined. The keys(), set(), and get() methods of the TransactionMap object can be used to process specific parameters contained in the map.
Transaction Processing
Executes the given transaction and processes all of the results returned. This method automatically clears all existing transaction results before executing the new transaction. After this method is called, the tx.status result will contain the transaction status, which should be checked by the client application before accessing any other results. If a transaction/map response is returned by the Skipjack Server, then all of the results will be automatically stored in the result map. Otherwise, the entire file returned by the Skipjack Server will be stored as a string in the respData result.
95 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Results Processing
Returns the value of the given result as a TransactionItem (single string value), TransactionList (list of values) or TransactionTable (table of values).
const char *getResultString (const char *key)
Returns the value of the given result as a String, automatically converting single values, list values and table values to a string.
int getResultInt (const char *key)
Returns a TransactionMap object containing all of the results that are currently defined. The keys(), set(), and get() methods of the TransactionMap object can be used to process specific parameters contained in the map.
In this case, the test/nop.tdf transaction is executed, using "admin:admin" to authenticate this client application. After the transaction is executed, the
96 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
result is examined as an integer value to determine if the transaction was successful. (By convention, tx.status values between 100 and 199 inclusive are considered successful.) If the status is not succesful, then an exception is thrown and the application exits.
tx.status
97 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
executes the test/ping.tdf which accepts the message input parameter, specified using the setParam() method, and returns the output.message result, which is accessed using the getResultString() method. In this example, the setClientID() method is used to specify a string that can be used to identify this client application in the server logs. If not specifically defined, a default client ID is generated based on the current process ID and host name.
Processing Results
In the following example, the sqlUtil/adhocSql transaction is executed to retrieve a table containing the names of all databases currently defined:
#include "Transaction.hh" #include "TransactionTable.hh" try { Transaction *tx = new Transaction(); TransactionTable *tbl;
98 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
int
rowCount, i;
tx->setAuthInfo ("admin:admin"); tx->setClientID ("myClient"); tx->setParam ("sqlExpr", "show databases"); tx->execute ("http://localhost:8080/sqlUtil/adhocSql.tdf"); int txStatus = tx->getResultInt ("tx.status"); if (txStatus < 100 || txStatus > 199) tx->error ("Tx Failure: %s\n%s\n", tx->getResultString("tx.status"), tx->getResultString("tx.errorMsg")); printf ("Database List\n"); printf ("-------------\n"); tbl = (TransactionTable *) tx->getResultItem ("output.table"); rowCount = tbl->getRowCount(); for (i=0; i < rowCount; i++) { printf ("%s\n", tbl->getValue(i,"Database")); } delete tx; } catch (TransactionException ex) { printf ("ERROR: %s\n", ex.getMessage()); exit (-1); }
In this case, the sqlExpr input parameter is defined, as required by the sqlUtil/adhocSql transaction and the output.table result is accessed. Since database queries can return multiple rows and columns, the result is accessed using the getResultItem() method, which returns the object for the output.table result. In this case, it is known to be a TransactionTable object, so that result is cast accordingly and each value in the "Database" column of that table is printed.
99 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
tx->execute ("http://localhost:8080/test/showTx.tdf"); int txStatus = tx->getResultInt ("tx.status"); if (txStatus < 100 || txStatus > 199) tx->error ("Tx Failure: %s\n%s\n", tx->getResultString("tx.status"), tx->getResultString("tx.errorMsg")); printf ("%s", tx->getResultString("respData")); delete tx; } catch (TransactionException ex) { printf ("ERROR: %s\n", ex.getMessage()); exit (-1); }
In this case, the text/html content type is specified, which causes the entire output file generated by the server to be stored as the respData result. Note, the tx.status value can still be accessed to determine if the transaction was successful, but no other transaction output parameters will be available.
100 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
The process begins with the Event Listener registering itself with the Event Manager (step 1), providing a Filter Criteria, an optional Event Handler Transaction and an optional Delivery Address. If a Delivery Address is specified, then the Event Listener waits to receive one or more Transaction Events from the Event Manager as they are processed. In normal operation, a Transaction Client submits a Transaction Request to the Transaction Servlet (step 2) and a Transaction Response is returned to the Transaction Client (step 3), allowing the Transaction Client to continue processing. Regardless of the number of Transaction Listeners registered for a given transaction, the transaction will be processed entirely and a response returned to the Transaction Client before any listener processing is begun. If the transaction was successful (as indicated by a status code of the form '1xx'), then the Transaction State is sent to the Event Manager for subsequent processing (step 4). The Transaction State consists of all TDF item attributes defined at the time that the transaction was completed.
101 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
For each Transaction State received, the Event Manager compares the transaction identifer to the Filter Criteria defined for each Transaction Listener that is currently registered. For each match found, an Event Worker thread is launched to process the event on behalf of the Event Listener. A fixed number of Event Worker threads are created when the Event Manager is initialized and are used on a first-come-first-served basis to process events on behalf of Event Listeners. If an Event Handler Transaction was specified when the Event Listener was registered, then it is executed by the Event Worker in order to process the event. The Event Handler Transaction can perform whatever processing is necessary based on the contents of the event, as will be discussed below. After the Event Handler Transaction has been processed, the Event Worker delivers the event to the Event Listener (step 5). The delivery mechanism, address and format are all specified by the Event Listener at registration time. At that point, the Event Listener application can perform additional processing as needed based on the event information. Unsuccessful transactions may also be delivered to the listener as needed by setting the ReceiveFailures parameter to "true" when registering the listener. That allows applications to monitor and optionally react to failure conditions for critical transactions.
Listener Registration
The eventMgr/add-listener.tdf transaction is used to register an event listener with the Event Manager, with the following input parameters supported: TxFilter GNU regular expression that is compared against the transction identifier to determine if the transaction event should be processed. TxName Optional name of the Event Handler Transaction to be executed when an event is processed. If no TxName attribute is defined, or it is the empty string, then no event handler transaction processing is performed. TxInput<XXXX> Optional input parameter for the Event Handler Transaction, which is used to define the input.<XXXX> attribute when the Event Handler Transaction is executed (i.e., the leading "TxInput" prefix is stripped). Any number of Event Handler Transaction input parameters can be specified by changing the value of <XXXX>.
102 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
AddrType Specifies the delivery mechanism to be used to send the event to the Event Listener application, with the following types supported: TCP (default) PROXY NONE The specific processing performed for each address type is described below. Host If AddrType is 'TCP' or 'PROXY', this attribute specifies the TCP host name or IP address at which the Event Listener resides. Port If AddrType is 'TCP', this attribute specifies the TCP port number to at which the Event Listener is waiting. MaxWait If AddrType is 'PROXY', this attribute specifies the maxium time that the Event Worker will wait for the Transaction Listener to connect before closing the server socket. OutputType, OutputFormat MIME type that indicates the output format of the Transaction Event sent to this listener, with the following values supported: transaction/map (default) template/file template/string The specific processing performed for each output type is described below. RemoveOnFailure If "true", then the Listener Registration will be deleted whenever the Event Manager fails to properly deliver a Transaction Event. Normally, such a failure indicates that the Event Listener application has been halted, so there is little reason to try again with subsequent events. The default value is "true". RemoveOnRestart If "true", then the Listener Registration will not be stored in the database and will not be automatically re-loaded when the server restarts. Normally, this is used by Event Listener applications that are automatically restarted when the Skipjack Server restarts (such as the internal Process Manager). The default value is "true". ReceiveFromSelf If "true", then the Event Listener application will receive Transaction Events that were originated from the same application, as
103 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
determined by the ClientID. The default value is "false". ReceiveFailures If "true", then the Event Listener application will receive Transaction Events for all transactions that match the filter criteria, even if they have an unsuccessful status. The default value is "false".
If the registration transaction is successful, a unique ListenerID is returned to the Event Listener application as the output.ListenerID attribute, which can be used to track the registration information while viewing the Event Monitor, Event Table, or Event Log.
Event Items
Prior to executing the event handler transaction, the following items are copied from the event being processed into the event handler transaction: Event Item Handler Item request input output tx eventRequest eventInput eventOutput eventTx
This allows the event handler transaction to reference all of the key state values from the original event as needed. For example, the event handler transaction may want to use the output and transaction status values of the event as input values, as in the following "input" item definition:
104 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
or they can be referenced directly in any attribute expression as needed, as in the following action definition:
item summarizeAsNeeded extends BaseAction { actions = eventOutput.X > 10 && eventOutput.Y > 20 ? ["doSummary"] : []; }
Finally, the event item attributes can be referenced directly by a Velocity template in order to generate a custom SQL expression or a detailed message that is emailed to someone, to name a few examples.
then the genAlarm transaction will be executed for every transaction event, with the following input parameters:
Mode = "ONFAILURE" ErrorCode = "s915"
In this case, the genAlarm transaction processes only failed transaction events based on the Mode input parameter and uses an error code of "s915" whenever an alarm is generated based on the ErrorCode input parameter. The exact number and meaning of event handler transaction input parameters depends on the implementation of each event handler transaction. Generally, event handler transactions should be designed to use default values for all expected inputs, but the client application is ultimately responsible for specifying the appropriate event
105 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Event Delivery
Independent of any Event Handler Transaction processing performed for a given event, the event can be delivered to a user or client application at a given address specified using the AddrType input parameters of the eventMgr/add-listener.tdf transaction. The AddrType parameter defines the type of delivery mechanism to be used and other address-specific parameters are used to define the delivery address itself.
106 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
When the 'TCP' address type is specified in a listener registration, the Event Worker will attempt to connect to a given IP address and port in order to deliver the event, based on the Host and Port registration parameters. If a successful connection is made, the Event Worker will format the event based on the OutputType and OutputFormat attributes and will send the resulting output stream to the Event Listener via the connection. After the output has been sent, the Event Worker shuts down the TCP connection. If an error occurs at any time during the delivery process, the RemoveOnFailure is used to determine if the listener registration should be retained or removed.
When the eventMgr/add-listener.tdf transaction is executed, it creates a new server thread, called the Proxy Listener, that opens a TCP Server Socket and stores the corresponding port in the Transaction Response that is returned for the add-listener.tdf transaction. The Proxy Listener then waits for a connection from the Transaction Listener application (the reverse of the approach described above). When the Proxy Listener receives a Transaction Event for delivery from an Event Worker, it relays it to the Event Listener application over the open connection. When registering a proxy listener, the MaxWait parameter can be used to specify the maximum time period in seconds that the Proxy Listener will wait for a connection from the Event Listener before aborting the process.
107 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
The TxFilter parameter provides the basic mechanism for specifying by name the transaction events that are desired by a given listener. However, in addition to that filtering, an event handler transaction can be used for customized or more advanced event transaction filtering. Specifically, if the event handler transaction determines that the given event should not be delivered to the listener, then it need only set the tx.abortEvent attribute to true to abort event delivery. For example, the following event handler transaction aborts delivery if the user who generated the transaction event is not "fred":
item abortEvent extends preCondition = _destItem = abortEvent = } ComputeAction { eventRequest.RemoteUser != "fred"; "tx"; true;
In this case, a preCondition is used to determine if the tx.abortEvent flag should be set to true to abort delivery of the event. Alternatively, the event handler transaction can simply throw an error, using an ErrorAction or main.preErrorCheck, which will also abort event delivery, as in the following example:
item main extends NOP { preErrorCheck = [ (eventRequest.RemoteUser != "fred", guiStatus.InvalidParameter, "The Event Was Not Generated By Fred") ]; }
The only difference between this approach and the previous one is that the error is appended to the event log, whereas the use of the tx.abortEvent flag aborts the event delivery without recording any log information.
Event Formatting
Whenever an event is delivered to an Event Listener, the Event Worker formats the event in the manner specified by the OutputType and OutputFormat attributes defined in the listener registration. This allows different applications to receive the same event in whatever format is convenient for that application. If the OutputType is 'template/map', then the "input", "output", and "tx" event items will be encoded using the TransMapResponse class, which generates an ASCII encoding of scalar values, lists and tables. This is useful for applications that want to perform additional processing based on the transaction item
108 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
attributes and are using one of the Skipjack Client libraries discussed below. If the OutputType is 'template/file', then the OutputFormat attribute is interpreted as the name of a Velocity template which is used to format the Transaction Event output stream. This is commonly used by applications that are generating event reports or that require a standard output format. If the OutputType is 'template/string', then the OutputFormat attribute is processed directly by Velocity as the template expression used to format the Transaction Event output stream. This allows applications to specify custom event formatting templates. If an Event Handler Transaction is specified for a given listener then the "input", "output", and "tx" items of the Event Handler Transaction are used for output formatting, not the corresponding items from the original transaction event. This allows Event Handler Transactions to generate appropriate values as needed. If the items from the original event are required for output formatting, then either the output template should referenced the "eventInput", "eventOutput" and "eventTx" items or the Event Handler Transaction should copy the necessary item attributes into its own "input", "output" and/or "tx" items.
Note, in all cases, the clientID associated with the Event Listener application is used to identify the corresponding listeners. Therefore, it is important for applications to use the same clientID when adding and removing listeners. By default, the Java, Perl and C++ Client Packages adhere to this approach.
109 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
The TransactionMonitor class (provided as part of the Java Client Package) implements a background thread that monitors transactions executed by the Skipjack Server and supports a collection of methods that allow client applications to easily register event listeners and process events as they are received. When a Transaction Event is received by the TransactionMonitor it is automatically converted into a Transaction object and passed to one or more TransactionObserver objects that have been registered with the TransactionMonitor. The TransactionMonitor object isolates the client application from all of the network and encoding tasks required to monitor one or more transactions executed on a given Skipjack Server. For example, the following Java application uses the TransactionMonitor to print the transaction identifier for every transaction that is executed for a period of two minutes.
import navius.skipjack.client.*; import navius.toolkit.util.*; import org.apache.log4j.*; public class SimpleMonitor implements TransactionObserver { public void transactionCompleted (Transaction tx) { System.out.println ("Tx = "+tx.getResultString("tx.identifier")); } public static void main (String[] args) { try { BasicConfigurator.configure(); TransactionMonitor tm = new TransactionMonitor ("tm.cfg"); SimpleMonitor sm = new SimpleMonitor(); tm.addObserver (sm); tm.start(); tm.addEventRequest (".*"); Thread.sleep(120000); } catch (Exception e) { e.printStackTrace(); } } } // End of Class
The main method begins by initializing the Log4j properties using the BasicConfigurator, which is used internally by the TransactionMonitor for debug generation. It then creates a TransactionMonitor object, based on the "tm.cfg" configuration file, which is defined as follows:
_debugEnabled: _transMapDebug: _serverURL: _authInfo: _clientID: _useProxy: false false http://localhost:8080/ test:PASSWORD test.SM false
110 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
The "tm.cfg" file specifies the server to be monitored, along with the authentication information needed. It also defines the listener registration parameters that are used when each addEventRequest() method is executed. The SimpleMonitor class implements the TransactionObserver interface, which allows it to be added as an observer of the TransactionMonitor object. In this case, it simply prints the transaction identifier when each transaction event is received.
111 of 150
http://205.177.219.178/doc/appDevGuide/printable.html
The eventMgr/show-table.tdf transaction can be used to display the current contents of the Event Listener Table in a browser for trouble-shooting purposes. The display shows the ListenerID, ClientID, Address and Criteria associated with each active listener.
112 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
113 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
handler transactions can be used for a wide range of application purposes, including application-level logging, database maintenance, operator notification, and application process constraints, to name a few. In many cases, such event handler transactions need to be registered when the Skipjack Server is started, thereby allowing them to process all transaction events whenever the server is running. This is easily done by specifying one or more Event Handler Items in a Process Definition File. Each Process Definition File can define one or more Event Handler Items that are referenced by name in the main.processList attribute. For every item name found in that list, the Process Manager will register a Transaction Event Handler using the eventMgr/add-listener.tdf transaction based on the attributes defined in the Event Handler Item. That is, each event handler is specified as an item that defines a set of input parameters for the eventMgr/add-listener.tdf transaction, with the following attributes supported: Attribute
TxFilter
Description Defines one or more GNU regular expressions that are applied to the transaction identifier of each transaction event. If a match is found, the event listener is executed for that transaction. If defined, this attribute specifies the event handler transaction to be executed whenever an event is processed. See the EventHandler class for more details. If defined, this attribute specifies the address to which the event should be sent. If the AddrType attribute is not defined, it will be set to "NONE". See the EventAddress class for more details. If the _clientID attribute is defined, it will be used as the client ID when the event handler transaction is registered and subsequently executed. Otherwise, the default client ID specified in the process.cfg configuration file will be used. If the _authInfo attribute is defined, it will be used as the username and password when the event handler transaction is registered and subsequently executed. Otherwise, the default authentication information specified in the process.cfg configuration file will be used. All attributes defined in the item that do not start with an underscore are copied into the input item when the eventMgr/add-listener.tdf transaction is executed, allowing arbitrary parameters to be passed to that transaction.
TxName
AddrType
_clientID
_authInfo
Other Attributes
For example, the following Process Definition File defines two process items, which specify a total of five event handler transactions:
item notifyProcess { TxName = "myapp/notifyOperator.tdf"; TxFilter = [ "myapp/addUser", "myapp/delUser" ]; }
114 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
item accountProcess { TxName = "accounting/logActivty.tdf"; TxFilter = [ "accounting/.*Credit", "accounting/.*Debit", "accounting/Deduction" ]; } item main { processList = ["notifyProcess", "accountProcess"]; }
The notifyProcess process item specifies that the myapp/notifyOperater.tdf transaction should be executed whenever the myapp/addUser or myapp/delUser transaction events are generated. The notifyOperator transaction could send an email message to a list of subscribers, possibly defined in the database, or could record the event activity in an application-level log table or file. The accountProcess process item specifies that the accounting/logActivity.tdf transaction should be executed whenever any accounting transaction ending with "Credit" or "Debit" generates an event, or when the accounting/Deduction transaction event is generated. The TxFilter attribute uses GNU Regular Expressions to specify matching criteria for transaction event identifiers.
Transaction Items
Each Process Definition File can define a list of transaction items as the main.startupList attribute to be executed when the Process Manager is initialized. Such transactions can be used for the initialization of application data structures, to log initial server information or any other server initialization purpose. Each Transaction Item specifies the name of the transaction to be executed, the meta information needed to execute the transaction and all input parameters for the transaction. In addition, each transaction item can specify one or more error checks, with associated error messages, that can be used to verify the correctness of the transaction output. Specifically, the following transaction item attributes are supported: Attribute
_transName _clientID
Description The _transName attribute specifies the identifier of the transaction to be executed. If the _clientID attribute is defined, it will be used as the client ID when the transaction is executed. Otherwise, the default client ID specified in the process.cfg configuration file will be used.
115 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
_authInfo
Other Attributes
If the _authInfo attribute is defined, it will be used as the username and password when the transaction is executed. Otherwise, the default authentication information specified in the process.cfg configuration file will be used. All attributes defined in the item that do not start with an underscore are passed along as input parameters when the transaction is executed.
For example, the following Transaction Items could be added to the previous Process Definition file to execute an initialization transaction and send an alarm to the operator when the server is restarted:
item notifyProcess { TxName = "myapp/notifyOperator.tdf"; TxFilter = [ "myapp/addUser", "myapp/delUser" ]; } item accountProcess { TxName = "accounting/logActivty.tdf"; TxFilter = [ "accounting/.*Credit", "accounting/.*Debit", "accounting/Deduction" ]; } item myInit { _transName Param1 Param2 } item genAlert { _transName Severity Message } = "myapp/initialize.tdf"; = "try this"; = 12;
Process Table
All threads that are dynamically created by server applications (including all background actions and startup processing threads) are registered in the Process Table that is maintained internally by the Process Manager. The Process Table can be used by operators and developers to monitor internal thread activity.
5/11/2007 10:14 AM
116 of 150
http://205.177.219.178/doc/appDevGuide/printable.html
The Process Table can also be used to manually terminate a thread that may not be working properly or that otherwise isn't needed anymore. The Process Table can be viewed by executing the processMgr/show-table.tdf transaction. When viewed in a browser, the table will be displayed as an HTML table, with one line per internal thread. Each thread is assigned a unique PID which can be used to track the thread over time. The PID can also be used to kill a thread using the processMgr/kill-process.tdf transaction. As a convenience, the HTML Process Table display includes a "delete" button on each row which can be used to kill the thread represented by that row. Threads registered in the Process Table should not normally be terminated manually, since they are automatically terminated when there work is done. The ability to kill a thread registered in the Process Table is provided primarily for advanced trouble-shooting purposes and should only be used by experienced operators. The HTML Process Table display also provides a "Reload" button that may be used to reload all Process Definition Files defined in the Process Manager configuration directory. This is provided primarily for developers working on Process Definition Files so that they can be tested without restarting the server.
Process Observers
External applications and applets can monitor the progress of any Skipjack process by registering an Event Listener using a transaction filter of "processTable/PID", where PID is the numeric ID associated with the process being monitored. After such a listener is registered, whenever the process table entry associated with the given process is added, modified or removed, the process observer will be notified. The navius.skipjack.client.ProcessObserver applet provides a convenient mechanism for monitoring one or more processes within an HTML page. By default, when all of the processes being monitored have been removed from the process table, then the browser will branch to a given URL. Generally, such a URL would then check to ensure that the process was successful or not, displaying an appropriate next page. The ProcessObserver applet can also be used to automatically kill the processes being monitored if the user leaves or overwrites the current page. See Event Listening for more details and examples about event handlers and listeners in Skipjack.
117 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server is started. All files ending in ".gdf" that are located in the directory will be processed as Process Definition Files. By default, the directory is defined to be $SKIPJACK/app/WEB-INF/process where $SKIPJACK is the Skipjack runtime directory. Application installation scripts need only copy Process Definition Files into that directory to have the corresponding event handlers registered when the server is started again. The standard $SKIPJACK/bin/CopyApp.pl application installation script automatically copies all files found in the process sub-directory of the application installation directory to the appropriate server runtime directory. Among the configuration variables specified in the process.cfg file are the default client ID and user authentication information that are used when registering and executing all event handler transactions. In most situations, the default configuration settings are sufficient for all event handler transactions. However, in some cases, it is necessary to use a different client ID, username or password to ensure that the event handler transaction works properly or can be more easily tracked in the server logs. In such cases, specific information can be defined using the _clientID and _authInfo attributes in a given process item. For example, the following definition:
item notifyProcess { TxName = "myapp/notifyOperator.tdf"; TxFilter = [ "myapp/addUser", "myapp/delUser" ]; _clientID = "NotifyProcess"; _authInfo = "notifier:mypassword"; }
overrides the default client ID and authentication information for the notifyOperator event handler transaction. That is, when that event handler is registered and executed, it will be assigned a client ID of "NotifyProcess" and will use the username of "notify" and a password of "mypassword". The process.cfg file also specifies the default settings for the RemoveOnRestart, RemoveOnFailure and ReceiveFromSelf attributes that are accepted by the
eventMgr/add-listener
transaction. The default settings have been chosen to be consistent with the way the Process Manager operates and should not need to be adjusted or customized for a given event handler.
118 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
Session Item
The session item is automatically created when using login forms (see Custom Login Forms for more details) or can be created by any transaction that needs to maintain client information across multiple transactions in the same session. If a session exists when an HTTP request is made, it will appear as an HttpSession object associated with that request when it is processed by the Skipjack Server. In such cases, the Skipjack Server will automatically create the session item and will assign a reference to the HttpSession object to the session.object attribute. Furthermore, it will copy all HttpSession attributes into the session item. Transactions can access any of the session attributes simply by referencing the session item attributes. If a transaction defines the session item in the transaction definition file or modifies the session item during the execution of the transaction, then any attributes defined for that item will be stored in the HttpSession object when transaction processing has been completed. If the session is defined, but no HttpSession object currently exists, a new HttpSession object will be created and associated with the current client session. This allows transactions to pass information from one transaction to another as the user travels around the site. Since the transaction definition file is processed before the HttpSession object, any session item attributes defined in the transaction definition file itself will be overwritten by HttpSession attributes with the same name. Transaction authors should use the ComputeAction to dynamically update session parameters sometime during transaction processing to ensure that those values will be assigned to the session when the transaction processing is completed. For example, the following transaction demonstrates how a session value can be modified:
119 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
item updateSession extends ComputeAction { _destItem = "session"; curValue = integer(session.curValue) + 10; curTime = date("now"); } item main extends BaseAction { actions = ["updateSession"]; templateName = "sessionTest.vm"; }
The first time the transaction is executed during a browser session, a new session object will be created, with the curValue attribute set to 0. Then, when the updateSession action is executed, the curValue session attribute will be incremented by 10 and the curTime session attribute will be set to the current date and time. Every time that the transaction is subsequently executed during the browser session, the curValue attribute will be incremented and returned to the browser. Keep in mind, even though the transaction definition always sets the session.curValue attribute to zero in the previous example, that value is overwritten by the current value defined in the session object provided by the client, if any. That is, the session item values defined in the transaction definition file are the initial values used whenever a session is created. The ComputeAction should be used update session values for existing sessions.
Session Timeout
The information stored in a session item can be automatically erased if the user does not perform any session activity in a certain time period. The default session inactivity timeout value can be specified in the $SKIPJACK_HOME/app/WEB-INF/conf/transaction.cfg file by the sessionTimeout property (in seconds), which is configured for a default value of 604,800 seconds (ie. 7 days). If a negative value is specified, then sessions will not timeout. In addition to the default timeout setting, any transaction can modify the timeout value for the current session by setting the _timeout attribute of the
session
item. When the transaction response is generated, any value specified by that attribute will be used as the timeout value for the session. If a negative value is specified, the session will not timeout.
120 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Session Invalidation
There are times when a transaction author needs to immediately invalidate a session. For example, the application may require the user to "logout" in order to safeguard information or the user may want to "empty" their shopping cart. In such cases the _invalidate attribute of the session item can be used. If that attribute is set to "true", then the session will be invalidated when the transaction response is generated. Any subsequent access made by the user will cause a new session to be created. For example, the following transaction causes the current user session to be invalidated:
// // test/formLogin/logout.tdf // include "skipjack-common.tdf" item session { _invalidate } = true;
item main extends BaseAction { actions = []; successMsg = "You Have Been Logged Out Of Your Current Session"; templateName = "/COMMON/tx-success.vm"; }
Notice that no actions are actually performed by this transaction, only the session._invalidate is set to "true", which causes the current session to be invalidated. If the current session was created as the result of a forms-based login screen and it is invalidated, then when the user attempts to execute another transaction the login form will automatically appear again, requiring them to provide their username and password.
Session Debugging
Session debugging is enabled for a given transaction by setting the debug.sessionDebug attribute to "true", as in the following example:
item debug { sessionDebug = true; }
When session debugging is enabled, the incoming and outgoing value of all session attributes will be logged in the $SKIPJACK_HOME/logs/trans.log file. See Debug Management for server-wide debug management techniques.
121 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
122 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
IP port (default = 21) FTP account username FTP account password binary or ascii (default = binary) remote file path
If the file path specified by the ftpInputFile attribute starts with '/', then it will be treated as an absolute path on the remote file system. Otherwise, the file path will be treated as a path relative to the default directory for the remote user specified by the ftpInputUsername attribute. VELOCITY The data stream is generated by the Velocity template engine, using the template specified by the velocityTemplate attribute and the current transaction state.
Record Parsing
The parseType attribute is used to determine how the data stream should be broken into records and fields, with the following types supported: DELIMITED The data stream is broken into records based on the following attributes: Attribute
delimRecord delimField
Delimiter used between fields within a record, consisting of one or more characters. If the | (pipe) keyword "FIXED_WIDTH" is specified then all records are assumed to contain fixed width fields as defined by the fieldWidths attribute of each record definition item (discussed below). Delimiter used to denote the start of a comment, consisting of one or more characters. All // (double-slash) characters following a comment delimiter will be ignored up to the next record delimiter. Delimiter used to escape another delimiter, consisting of one or more characters. Any character following the escape delimiter will not be considered as part of another delimiter. If true, then leading and trailing whitespace for all fields will be removed. \ (back-slash)
delimComment
delimEscape
delimTrim
true
123 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Record Validation
To support the validation of heterogeneous record formats defined in the same file, the recordMap attribute is used to specify the names of one or more record definition items that determine how each record should be validated, processed and stored for further processing. Each record definition item may specify the following attributes: acceptExpr schemaName actions fieldWidths For every record found in the data record stream, the following processing will be performed for every record definition item defined in the recordMap list: 1. A temporary item named curRecord will be created using the attributes defined by the schema item referenced by the schemaName attribute, assigning the values found in the current input fields to each attribute. For example, if the schema defines three attributes: A, B and C, then the curRecord item will also have three attributes named A, B and C, with values assigned from the input record. Additionally, the RecordType, RecordNumber, RecordString and RecordFile meta attributes will be defined as described below. If "FIXED_WIDTH" was specified as the delimField value in the import action item, then the fieldWidths attribute must be specified in each record definition item, defining a list of field widths. As each record definition item in the recordMap is processed for a given input record, the field width list associated with that record definition item will be used to break the record into fields. Otherwise, the field delimiter specified by the delimField attribute of the import action item will be used to determine the field values for all record definition items. 2. The acceptExpr of the record definition item will be evaluated as a boolean value. If it is false, then no further processing of the curRecord will be performed for the current record definition item. If it is true, then the current record is "accepted" by the current record definition item and no subsequent record definition items will be processed for that record. 3. If the acceptExpr is true, then the schema action defined by the schemaName attribute will be executed to validate all of the attributes in the curRecord item. If a validation exception is thrown, then no further processing will be performed for the current record definition item. 4. If schema validation was successful, then all of the actions defined in the actions list (if any) will be performed. Each action can reference the
curRecord
124 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
as needed to perform additional validation or custom data conversion. If any of the actions throws an exception, then no further processing will be performed for the current record definition item. If the curRecord is accepted by a record definition item, the schema validation was successful and the actions list was successfully executed, then the curRecord is appended to the Table object specified by the _destItem and _destTable attributes of the record definition item. If those attributes are not defined, the record definition item itself will be used as the value of the _destItem attribute and "table" will be used as the value of _destTable attribute. If the curRecord is not accepted by any record definition item or an exception is thrown during schema validation or action list processing, then an error entry will be added to a Table object that is stored as the value of the errorTable attribute of the import action item itself.
RecordNumber Record number in the defining file RecordString RecordType Record definition string from the file Name of the record definition item that accepted the record
The meta columns can be used by the transaction for subsequent record processing as needed.
125 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Unlike most actions, the ImportAction does not automatically cause the transaction to abort as soon as an exception is raised. Rather, all exceptions raised while processing records are captured and stored in the errorTable, thereby allowing transactions to deal with each error individually if necessary. The error table consists of all of the meta columns defined above, as well as the following error columns: Column Name ErrorStatus ErrorMessage Description Value of the tx.status attribute at the time the error was generated Value of the tx.errorMsg attribute at the time the error was generated or the message string of any exception that was thrown
Record Processing
All of records imported using the ImportAction can be processed either by specifying a list of one or more actions for the actions attribute of each record definition item or by executing transactions after the ImportAction that directly access the record tables. In either case, the transaction author is free to specify whatever processing is necessary to perform custom validation or record processing. The only real distinction between an action that is performed as part of the actions list for a record definition item and an action that is performed after the ImportAction has completed is how errors are handled. In the former case, any exception that is thrown will automatically cause an entry to be appended to the error table and the current record to be discarded. In the latter case, the transaction author must handle exceptions themselves. Record processing actions defined in the actions list of the record definition item also have the benefit of accessing the curRecord item directly, instead of having to access fields in the Table object. Usually, this makes record processing easier to specify. The curRecord item is appended to the record table associated with a given record definition item after all actions defined in the actions list for that record definition item are executed. Therefore, if any of the actions alter the contents of the curRecord item (using the ComputeAction for example), then those changes will be stored in the record table, which may or may not be desirable depending on the application.
126 of 150
http://205.177.219.178/doc/appDevGuide/printable.html
defined so that it is relatively easy to extend that action and import a file containing records with the same format. That is, the ImportAction item itself (or any item extended from it) can be used as both the import action item and the record definition item. For example, consider the following data file:
// // tutorial/dataImport/ex1.dat // Fred Smith|78000|410-555-1212|Bldg 8, Rm 301 Sally Jane|84000|410-555-7878 Oscar Van-Pelt|87350.56|301-444-1867|Home
In this example, a collection of employee records are being imported from the ex1.dat file from the server's local file system. (Accessing a file using FTP will be shown in a subsequent example.) Since the data file contains only one type of record, we can combine the import action item and the record definition item into a single item, as in the following transaction:
// // tutorial/dataImport/ex1.tdf // include "skipjack-common.tdf" item importData extends inputType fileInputFile schemaName } ImportAction { = "FILE"; = "ex1.dat"; = "recordSchema";
schema recordSchema extends BaseSchema { attrs = [ ("Name", "string", "nonempty"), ("Salary", "double", "nonempty"), ("Phone", "phoneno", "nonempty"), ("Office", "string", "optional") ]; } item recordList { table hiddenFields borderColor borderWidth } item main extends BaseAction { actions = ["importData"]; templateName = "ex1.vm"; } = importData.table; = ["RecordFile", "RecordNumber", "RecordString", "RecordType"]; = "black"; = 1;
The importData action item is defined to perform the import processing, extending the standard ImportAction defined in the skipjack-common.tdf include file. The importData item specifies that the "FILE" input type will be used to access the file and that the recordSchema item will be used to validate all records contained in that file.
127 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
The ImportAction specifies a parseType of DELIMITED and a set of default delimiters to be used, which suffices for this example. The ImportAction specifies a default recordMap list which contains only a reference to itself and a default acceptExpr equal to "true". This allows the import action item to serve as the record definition item as well. When the importData action is executed by the main action, it accesses the designated file and parses it using the designated delimiters. For each record found, the recordSchema item is used to validate the record. The recordSchema item requires the record to contain the Name, Salary and Phone attributes, with an optional Office attribute. If record validation is successful, that record will be added to the importData.table record table. Otherwise, an error entry will be added to the importData.errorTable error table. The item used to store the record and error tables can be changed in this example by specifying the importData._destItem attribute. In this example, all of the records are valid and are stored in the importData.table record table. The recordList item references that table so that the listShow() macro in the ex1.vm template can display the table of records.
## ## tutorial/dataImport/ex1.vm ## #htmlPageHeader("Data Import Example 1") <center> #listShow($recordList) </center> #htmlPageFooter()
Note how the hiddenFields attribute is used to hide the meta table columns when the table is displayed. (The output can be seen at Data Import Example 1 if you are viewing this documentation directly from a Skipjack Server.)
128 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
//END //END
In this case, there is no field delimiter used to separate the field values, only a predefined width for each field. To accomplish such import parsing, the delimField attribute should be defined as "FIXED_WIDTH" and the fieldWidths attribute should be defined as a list of integer field widths, as follows:
item importData extends inputType fileInputFile schemaName delimField fieldWidths } ImportAction { = "FILE"; = "ex5.dat"; = "recordSchema"; = "FIXED_WIDTH"; = [14, 8, 12, 16];
With only these modifications, the fixed width import file can be processed as in the previous example.
129 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
// // tutorial/dataImport/ex2.tdf // schema recordSchema extends BaseSchema { attrs = [ ("Name", "string", "nonempty"), ("Salary", "double", "nonempty"), ("Phone", "phoneno", "nonempty"), ("Office", "string", "optional") ]; postErrorCheck = [ (curRecord.Salary < 80000, guiStatus.InvalidParameter, "The Salary Field In Record #" + curRecord.RecordNumber + " From The Import File " + curRecord.RecordFile + " Is Too Small, Give " + curRecord.Name + " A Raise."), (curRecord.Phone->startsWith("410") == false, guiStatus.InvalidParameter, "Invalid Area Code") ]; }
In this case, two error checks are performed based on various fields in the curRecord item. Specifically, an error is generated for a given record if the Salary field is less than 80,000 or the Phone field does not begin with the 410 area code. If an error is generated by the postErrorCheck criteria for a given record, then the resulting error status and error message will be appended to the importData.errorTable error table and the record will not be added to the importData.table record table. Note how the curRecord meta fields are used to generate a very specific salary error message. Usually, when more specific information is provided in error messages it is easier to resolve the problem.
130 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
item checkName extends SQLRecordAction { sqlFile = "checkName.sql"; Name = curRecord.Name; zeroRowsMsg = "Invalid Employee Name '" + Name + "' Found In Record #" + curRecord.RecordNumber; _destItem = "employeeRecord"; }
In this case, the checkName SQL action has been defined to validate the Name of each record processed. If the name is invalid, then the error thrown by the checkName action will cause all further processing to be aborted for that record and the error status and message will be appended to the importData.errorTable error table. Any number of custom validation actions can be specified in the actions list of the record definition item. Each action will be performed in the order given, until all actions have completed successfully or until the first action throws an exception. Each action can access the curRecord item as needed to accomplish its task (as well as any other transaction item defined).
131 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
In this case, the formatSalary action is used to format the value of the Salary field as a money field and store the result in the curRecord item, so that it is subsequently stored in the importData.table data table. Since the formatSalary action is listed after the checkName action in the actions list, it will only be performed for records that have been successfully validated by both the recordSchema action (including any custom error checks) and the checkName action. Similar formatting could be performed by either defining a storage format expression for the Salary attribute (see Input Validation for an example) or by executing the TableFormatAction after all import processing has been completed. Each approach has different advantages depending on the application and transaction developer preferences.
Usually, when different records are defined in the same data file they contain one or more fields that are used to determine the appropriate record type. In this example, the first field contains the record type, thereby making the acceptance criteria very easy to specify, such as:
item importData extends ImportAction { ... recordMap = ["empDefn", "supDefn"]; } item empDefn { acceptExpr = curRecord.Type == "EMPLOYEE"; ... } item supDefn {
132 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
The actual field names used for the curRecord item being processed by each record definition item depend on the list of attributes defined in the corresponding schema. Usually, the record type field is assigned the same name in all schemas to avoid confusion, but that's not a requirement. Clearly, each record type needs a different schema definition in order to properly name and validate all of the record fields. In this example, the following schema definitions suffice:
item empDefn { acceptExpr = curRecord.Type == "EMPLOYEE"; schemaName = "empSchema"; ... } schema empSchema extends BaseSchema { attrs = [ ("Type", ["EMPLOYEE"], ("Name", "string", ("Salary", "double", ("Phone", "phoneno", ("Office", "string", ]; }
item supDefn { acceptExpr = curRecord.Type == "SUPERVISOR"; schemaName = "supSchema"; ... } schema supSchema extends BaseSchema { attrs = [ ("Type", ["SUPERVISOR"], ("Name", "string", ("Dept", "string", ("Phone", "phoneno", ]; }
Note how the Type field is defined by each schema, specifying a single enumerated value as a valid input. (In this case, the Type field can never be invalid, because the acceptExpr ensures that only the proper record type is accepted, but that might not always be the case.) Note also how the schemas define record fields with different types and names, as well as a different number of fields. This allows transaction authors to process and store records using the most appropriate names and types, instead of having to invent generic field names like "field1", "field2", etc. Since multiple records are being imported, we need to store them in different tables. By default, the resulting data tables are stored as the table
5/11/2007 10:14 AM
133 of 150
http://205.177.219.178/doc/appDevGuide/printable.html
attribute of the corresponding record definition item. In the following example we specify a common results item, with appropriate table names for both of the data tables:
item empDefn { acceptExpr schemaName _destItem _destTable } item supDefn { acceptExpr schemaName _destItem _destTable } = = = = curRecord.Type == "EMPLOYEE"; "empSchema"; "results"; "empTable";
= = = =
Clearly, this example can be expanded to any number of different record types relatively easily.
134 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Skipjack Server
5 October 2002
135 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
("FirstName", ("LastName", ("Age", ("Married", ("State", ("Zipcode", ]; stateList } item myInfo { FirstName LastName Age Married State Zipcode } = = = = = =
= UnitedStates("ABBR LIST");
The formItem defines a simple form that consists of the given title and one entry field for each of the six attributes defined in the mySchema schema item, with the following output:
136 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
The overall width of the form is constrained to be 400 pixels by the formItem.boxWidth attribute and is centered on the page by the <center> HTML tag in the template file. Since the myInfo data item was referenced in the formShow() macro invocation, the attribute values defined in that item will be used to override any default attribute values specified by the schema item. If the second argument to the formShow() macro is specified as the empty string (""), then the default attribute values defined in the schema item will be used as the default values for the form fields.
Form Action
The previous example defined the formItem form definition item as an extension of the FormAction action item defined in the skipjack-common.tdf file. The
FormAction
action item defines a default set of attributes for all form items and it performs various form initialization functions when it is executed. As shown in the main item of the previous example:
item main extends BaseAction { actions = ["formItem"]; templateName = "simpleForm.vm"; }
the formItem is executed as part of the main action list. (Usually, form items are executed last, after all other transaction actions have been performed, thereby allowing the form to access all information generated during the transaction.)
137 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
If the form definition item is not executed by the transaction then an alert box will be displayed on the user's browser when the transaction response page is loaded indicating that the form definition item was not initialized properly.
Since the guiLabel item is defined in the standard Skipjack definition files, the "overrides" directive is used to modify that item in the current transaction file. In this case, we define three labels by specifying the form field name as an attribute name in the guiLabel item and the form field label as the corresponding attribute value, resulting in the following form being displayed:
The guiLabel item is used by a number of Skipjack features to specify labels for such things as form fields, database table columns and HTML table columns. In general, applications should define a single guiLabel item that defines all of the labels needed for that application and then include that file in all transaction definition
138 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Description If defined, the given title is displayed inside of the form border, centered at the top of the form. If defined, the given set of instructions is displayed at the top of the form, just below the title, if any. If defined, this attribute should reference an item that extends the BaseSchema item, defining all of the input fields to be displayed in the form as schema attributes. The type of HTML widget used for each form field will depend on the type of attribute specified in the schema. See Form Field Types below for more details. If the inputItem macro parameter is defined when the formShow() macro is invoked and it contains an attribute with the same name as a given field, then the value of that inputItem attribute will be used as the default value of the field. Otherwise, any default value for the field defined in the schema will be used.
buttons
If defined, this attribute should define a Vector of one or more button item names, where each string value in the list should reference a button definition item. See Button Definition Items below for more details. If defined, this attribute should define a Vector of schema attribute names. Each field referenced in the list will be displayed as a readonly field if a default value is defined for that field, either by the inputItem macro parameter or by the default value in the schema definition item. This allows transaction authors to prevent the user from updating critical fields, such as the unique ID of a database record. If defined, this attribute should define a Vector of schema attribute names. Each field referenced in the list will be generated as a HTML hidden form field if a default value is defined for that field, either by the inputItem macro parameter or by the default value in the schema definition item. This allows tranasction authors to hide form fields from the user, while still POSTing that information when the form is submitted. NOTE: Additional hidden fields can be defined for a form by using the hiddenItems attribute as described in the Form Control Attributes section below.
readonlyFields
hiddenFields
fieldSize
The field size (in columns) for all input fields displayed in the form. The default value is 40.
139 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
focusField boxWidth
If defined, the form field name referenced by this attribute will be given the focus as soon as the form is loaded. The overall width of the form in pixels. If not defined, the form will be as wide as necessary to accomodate all fields. NOTE: If an instruction is defined, then it may span the entire page width by default. In that case, use this attribute to limit the total width of the form.
The width (in pixels) and color of the form border. The gap width (in pixels) between the outer border and the form components, and the background color of the form.
file
file
140 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Choice Properties
When an enumerated list is specified as the attribute type in a schema item, a choice field will be generated in the form as either a selection list (the HTML <SELECT> tag) or a set of radio buttons. By default, a set of radio buttons is displayed if the number of choices is six or less, and a selection list is displayed if more than six choices are available. For example, consider the following schema definition and resulting form:
schema formSchema extends BaseSchema { attrs = [ ("ColorList", colorList, "default", ""), ("StateList", stateList, "default", "") ]; colorList = ["red", "yellow", "blue", "gold", "green"]; stateList = UnitedStates("ABBR LIST"); }
By default, the short color list is displayed as a set of radio buttons, with three columns of buttons per row. The number of columns can be specified using the xxxRadioCols schema item attribute, where xxx is the attribute name. The long state list is displayed by default as a drop-down choice list. If a selection list is desired, then the xxxChoiceSize schema item attribute can be used to specify the number of choices to be displayed at a time, where xxx is the attribute name. For example, the following attribute definitions
141 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Choice Labels
The xxxChoiceLabels schema item attribute can be used to define a different set of labels to be displayed in a choice field, while still reporting the actual choice value when the form is submitted. For example, if we want the color values to be displayed as upper case labels, while still reporting them as lower case, we could modify the previous example as follows:
schema formSchema extends BaseSchema { attrs = [ ("ColorList", colorList, "default", ""), ("StateList", stateList, "default", "") ]; colorList = ["red", "yellow", "blue", "gold", "green"]; stateList = UnitedStates("ABBR LIST"); StateListChoiceSize = 5; ColorListRadioCols = 2; ColorListChoiceLabels = ["RED", "YELLOW", "NAVY BLUE", "NAVY GOLD", "ARMY GREEN"]; }
The label list must be in the same order as the value list specified for the corresponding attribute in the schema item.
142 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Choice Type
The xxxChoiceType schema item attribute can be used to force a choice field to be displayed in a specific way ("select" or "radio", regardless of the number of choice items. For example, we can reverse the types of choice fields displayed in the previous example as follows:
schema formSchema extends BaseSchema { attrs = [ ("ColorList", colorList, "default", ""), ("StateList", stateList, "default", "") ]; colorList = ["red", "yellow", "blue", "gold", "green"]; stateList = UnitedStates("ABBR LIST"); StateListChoiceSize = 5; ColorListRadioCols = 2; ColorListChoiceLabels = ["RED", "YELLOW", "NAVY BLUE", "NAVY GOLD", "ARMY GREEN"]; StateListChoiceType = "radio"; StateListRadioCols = 5; ColorListChoiceType = "select"; }
143 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
schema formSchema extends BaseSchema { attrs = [ ("ColorList", colorList, "default", ""), ("StateList", stateList, "default", "") ]; colorList = ["red", "yellow", "blue", "gold", "green"]; stateList = UnitedStates("ABBR LIST"); StateListChoiceSize = 5; ColorListRadioCols = 2; ColorListChoiceLabels = ["RED", "YELLOW", "NAVY BLUE", "NAVY GOLD", "ARMY GREEN"]; StateListChoiceMulti = true; StateListChoiceSize = 10; ColorListChoiceType = "select"; }
In most browsers, the user should hold the Control key when selecting an item in order to select or deselect that item without changing the other selections. The Shift key can also be used to select all items between the last item selected and the current item being selected.
144 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Button image files can be created using any graphics program, thereby allowing a wide range of look and feels. Basic rectangular buttons can also be generated using the navius.apps.ButtonMaker application. Each item in the buttons Vector should be the name of a Button Definition Item which specifies the properties and behavior of each button, with the following attributes supported: Attribute
buttonName buttonImage
Description Name of the button Specifies the prefix of the image files to be used to represent the button. If buttonImage is not defined, then the buttonName is used. The actual image file names are determined by appending "-button.gif" and "-recess.gif" to the specified image name. URL to which the form should be submitted when the button is pressed. JavaScript command to be executed when the button is pressed. If this attribute is specified, the actionURL attribute is ignored. Message to be displayed on the browser status line when the mouse hovers over the button
actionURL actionFunc
helpMsg
Description Specifies the 'action' property of the HTML <FORM> tag Specifies the 'target' property of the HTML <FORM> tag Specifies the 'enctype' property of the HTML <FORM> tag Specifies a Vector of one or more item names. For each item referenced in the list, all of the attributes defined in that item will be created as hidden form fields. In each case, the attribute name will be used as the form field name and the attribute value will be used as the form field value. This allows transaction authors to pass hidden data to the action when the form is submitted.
145 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
hiddenInputItem
Specifies the name of an item that should be used to define hidden fields for all of the input parameters of the transaction that generated the form. When the form action item is executed, it will automatically create the item referenced by this attribute and copy all input attribute names and values into that item. When the HTML form is generated, hidden form fields will be created for each attribute copied into that item. When copying the input attributes, the form action will not copy any input attributes that are already defined as either normal or hidden form fields. That is, it will skip any input attribute that has the same name as any of the form fields specified in the form schema attribute list or any of the form fields specified in the items specified by the hiddenItems list. This allows transaction authors to make use of whatever fields are needed by the form, while passing all input parameters through the form to the next transaction.
formID
If this attribute is defined, a hidden field named '_formID' will be created with the value given. The _formID field can be used by transactions to determine from which form they were executed. If this attribute is defined, then a hidden field named '_formStack' will be created with that value. The _formStack field is used by the standard JavaScript functions created with every form to automatically execute the "previous" form. Specifically, the form stack maintains a chain of transaction identifiers that can be used to "return" from a form to the form that originally executed it. The _formStack field is automatically maintained by the Form Action. This attribute is used to "set" the stack to a known starting transaction. Generally, top level forms will set this attribute and all lower-level forms will use the automatic settings.
formStack
146 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Attribute
layout
Description If "vertical" is specified, then all buttons will be displayed as one or more vertical columns. Otherwise, all buttons will be displayed as one or more horizontal rows. The default value is "horizontal". Vector of one or more Button Definition Item names. If the special name "BREAK" is specified, then a blank space will be inserted in vertical layout mode and a new line of buttons will be started in horizontal layout mode. If this attribute is defined, then it should specify the name of a form definition item to be used for all menu buttons instead of the formItem parameter specified when the macro was invoked.
buttons
formItem
For example, the following menu definition item results in the following menu being displayed:
item formMenu { layout = "horizontal"; buttons = ["insertButton", "deleteButton", "editButton", "BREAK", "okButton", "cancelButton"]; }
147 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
page loading. Specifically, it generates the necessary HTML tags to include the "skipjackForm.js" file, which defines all of the JavaScript functions used by the form macros. This macro is automatically invoked by the default htmlPageHeader() macro that is used by all Skipjack Server page templates.
148 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
Although the form is functional, we might want to specify a custom set of field labels using the guiLabel item as follows:
item guiLabel overrides guiLabel { _readonly = true; who = "To Whom"; fontSize = "Size of Font"; bgColor = "BG Color"; }
and perhaps specify a bunch of display properties (doesn't everyone love being a graphics expert!) by overriding the inputForm item as follows:
item inputForm overrides inputForm { title = "Hello World Input Form"; instructions = "Please fill out the following fields and press" + " OK to display your message:"; buttons boxWidth outerColor outerWidth innerColor fieldSize } item returnButton { buttonName = "return"; actionFunc = "history.back()"; helpMsg = "Return To Previous Page"; } = ["inputFormSubmitButton", "returnButton"]; = = = = = 250; "black"; 1; "lightgreen"; 10;
In addition to the cosmetic changes, we also re-defined the list of buttons displayed on the form to include a "return" button. The resulting customized input form would be displayed as follows:
149 of 150
5/11/2007 10:14 AM
http://205.177.219.178/doc/appDevGuide/printable.html
150 of 150
5/11/2007 10:14 AM