User-compiled Functions
SCRIBE allows the end user to encapsulate a piece of logic into a box called "User-defined Function" and reuse it in various Reports and Processes. SCRIBE also provides an inventory of the most commonly used functions that are ready for use the moment SCRIBE is installed.
Although user-defined functions can be assembled from a rich assortment of pre-fabricated objects that address most of the database communication, text and numbers manipulation, decision-making etc. problems, there may come a time when the user will wish to construct their own piece of code not limited to the functionality available to the user-defined functions from the inventory of graphical tools.
SCRIBE provides for this by allowing the end users to compile their own Java code into a class and incorporate it into SCRIBE by dynamically loading the class at the launch time. The loading is done automatically, once the class is copied into FunctionClasses folder of the Server installation directory. This pre-compiled class appears in SCRIBE as a new function that can be used like any other user-defined or system-provided function.
In order to work properly, the user-compiled function must conform to the rules described in the following section.
| 1. | General structure of functions |
Once brought into the Workspace from the Palette window, the functions expand into the following generic graphical form:
In its most general form, the function can take a number of input parameters, and return one or more values back to the graphical flowchart (Report or Process) where it is located. Variations are possible, when a function does not return any value, or does not take any parameters. Sometimes functions may return a single value through the slot provided at the bottom of the graphic, and sometimes they can return multiple values through the parameter slots (in which case they become input/output, rather than just input, parameters).
Ultimately, the function may not have any parameters and not return any value; such function may nevertheless serve a useful purpose that is pre-programmed in its body and simply does not need to communicate with the rest of the world in order to do its job.
Whatever shape and form the functions have, they all must extend an abstract superclass, SystemFunction. SystemFunction provides a basic framework, initializing variables and objects (among them the SFDataBase object that provides a communications channel with the supporting relational database), and implementing a number of methods that can be overridden by the subclasses.
| 2. | Makeup of function's parameters and return value |
Every function that takes one or more parameters or returns a value must implement a constructor method where it creates a Vector of ParameterData objects (one for each input parameter that the function takes) and instantiate a Variable object that will carry the return data back to the caller. The following is a sample code taken from the SystemStateFunction (the system function that returns an integer number of system state as recorded in one of the SCRIBE database tables):
public static SystemStateFunction getInstance()
{
if (instance == null)
instance = new SystemStateFunction();
return (instance);
}
protected SystemStateFunction()
{
returnVariable = new Variable("",
Variable.DATAFIELD);
returnVariable.setDataType(INTEGER);
/*
** Create Vector for parameter data and fill it in
*/
parameterList = new Vector();
ParameterData parameter =
new ParameterData("Date", DATE, DATE, INPUT,
true, true);
parameterList.addElement(parameter);
parameter = new ParameterData("Time",
TIME, TIME, INPUT, true, true);
parameterList.addElement(parameter);
}
Every object of parameterList Vector carries information about one parameter taken by the function. The following is a brief description of the ParameterData class constructor method that takes this information:
public ParameterData(String name, int dataType, int displayedDataType, int
ioFlag, boolean runTime,
boolean isSwitchDisplayed)
| name | name that appears in the parameter slot of the graphic when the function is brought into the Workspace. | |
| dataType | type of data carried by the parameter (see ParameterData class documentation for the complete list of all available data types). | |
| displayedDataType | type of data used in formatting the legend of the function (usually the same as dataType). | |
| ioFlag | flag to tell the system whether or not the function can return data through the parameter slot, in addition to taking an input value from it (see ParameterData class documentation for the list of valid I/O flags). | |
| runTime | determines how the parameter is presented in the function's graphic. If the run time flag is set to true, the parameter slot in the function's graphic is set to receive the name of the variable that carries data. If the flag is set to false, the parameter slot is set to be an editable field in which the actual data is typed. This flag only determines the initial state of the parameter slot; it can be changed by pressing the run-time switch (see isSwitchDisplayed flag in the next entry). | |
| isSwitchDisplayed | controls the use of the run-time switch. If this flag is set to false, the switch is not displayed and the parameter slot is set to the rigidly fixed state, determined by the runTime flag. |
ParameterData class implements one more method to set Vector of String objects:
public void setStringTable(Vector stringTable)
| stringTable | Vector object that can be used to carry items for pop-up list to be displayed in the parameter slot. In this case, when the parameter is set to an actual data contents (i.e. runTime flag is false), it will display a pop-up list of fixed values instead of an editable text field. Use of this method is optional. |
If the function does not take any parameters, it does not have to instantiate the parameterList.
To pass the results of its work to the caller, the function can instantiate returnVariable object (of Variable class) that must be set to the appropriate data type (see ParameterData class documentation for the complete list of all available data types). Functions that do not return values, or return them through parameters, do not need to instantiate the returnVariable object.
| 3. | Methods implemented by functions |
In addition to constructor method described in the previous section, subclasses of SystemFunction must implement the following methods:
public String functionName()
This method returns the String that is shown in the list of available functions in the Palette window, when the user brings in the new function into the Workspace. This is the name by which the function is recognized in the flowchart where it is located.
public String descriptionForFunction(String name)
This methods formats and returns the String that is shown in Description column of the function list displayed by the Palette. name is the name of the function as set by the functionName method and may be ignored.
public int returnType()
Returns the data type of returnVariable object as set by the constructor method; needs to be implemented only if the function returns a value.
public Variable execute(Vector parmList)
This is the central method of every function. It is in this method that the function does its work (calculation, formatting of text, retrievals from and updates to the database tables etc.) parmList is a Vector of Variable objects that contain the parameter data upon which the function operates (functions that do not use parameters receive null as parmList).
In order to extract data from its parameter list, the function can use the following instance methods implemented by the Variable class:
public String asciiValue()
Returns textual representation of data carried by the Variable. Values of LOGICAL type are returned as "YES" or "NO" Strings. Values of IMAGE type are not returned; instead the method returns "<Image>" String.
public String stringValue()
public int intValue()
public short shortValue()
public byte tinyValue()
public double doubleValue()
public boolean boolValue()
Returns the value carried by the Variable.
public void setValue(int number)
public void setValue(short number)
public void setValue(byte number)
public void setValue(double number)
public void setValue(boolean flag)
public void setValue(String str)
public void setValue(byte[] array)
Sets Variable's contents to the given parameter value
public void setDataType(int type)
Sets the data type of the Variable. Valid data types are listed in ParameterData class documentation.
public int dataType()
Returns the type of data carried by the Variable. It is the same data type that is set by setDataType method.
Every implementation of execute method must begin with sending this message to its superclass:
super.execute(null);
Functions returning a value must return their returnVariable object in the return statement as follows:
return (returnVariable);
When execute method is called the first time, its super.execute(null) call obtains an initialized instance of the SFDataBase object from SCRIBE in the database instance variable. This object can be used to retrieve, update, insert and delete rows in the database tables. The SFDataBase object cannot be instantiated by the function; rather, it is created by SCRIBE at the launch time. There is exactly one instance of the SFDataBase object in every running copy of SCRIBE.
| 4. | Singly instantiable functions |
Sometimes it is desirable to create a single instance of the function, so that all graphics showing the same function in the given Procedure, Report or Process actually use the same object. In such cases the user should implement a singleton pattern with the following methods and static variables:
private static CurrentUserFunction instance = null; /* Id of the instance of the Class (one only) */
public static CurrentUserFunction getInstance()
{
if (instance == null)
![]()
instance = new CurrentUserFunction();
return (instance);
}
See examples of functions for more information.
| 5. | Building the function class |
In order to be dynamically loaded into SCRIBE at the run time, functions must be compiled into classes. The classes are stored in the designated folder in the SCRIBE Server installation directory (FunctionClasses) and are picked up from there by Java classloader mechanism when SCRIBE is launched.
The compiler's classpath must include Server.jar file. The file can be found in the Scribe Server installation directory.
| 6. | Troubleshooting |
Once loaded into SCRIBE, the function becomes part and parcel of the executable code. Hence, run-time errors caused by the execution of the function may have far-reaching ripple effect in SCRIBE. To help track errors, SCRIBE mainatins an error log that can be turned on: