Custom Functions
Testing the
Java Program outside of Ebase
Adding your Java functions to the
Ebase server CLASSPATH
See also:
Understanding Ebase Integration
Overview
The Ebase form processing language (FPL) can be extended
with user custom functions. Once a custom function is made available to Ebase
it can be used within any processing script. As with FPL system provided
functions e.g. uppercase, lowercase etc, custom functions
can be nested with other functions, can appear in any expression, and may take any Ebase fields
or other functions as parameters.
e.g. if repayment(loan, years, interestRate,
mortgageType) is defined as a custom function then valid usage within FPL
would include
set
field1 = repayment(field1, field2, 5.0, field3);
if
[repayment(field1, field2, 5.0, field3) > Field5] then ...
set
Premium = repayment(field1, field2, 5.0, round(field3)) + F6;
Each custom function is mapped to a Java class that exists
outside of the Ebase environment. Whenever the Ebase form processor comes
across a custom function during form script execution it will automatically
call the mapped Java class and pass any defined parameters. Ebase achieves this
by instantiating the Java Class which must be written to a specific
specification (see below) and executing a
run() method on that class. The run() method contains the programming logic to
execute the custom function.
Custom functions enable data from any application that
exists outside of the Ebase environment to be incorporated into form processing
logic. Equally, they allow data to be written directly from an Ebase form to
any external application. Custom functions are called from FPL scripts which
themselves are executed via the Event
Model implemented by Ebase.
Because a custom function is mapped to Java class, then the
function may implement as much connectivity and processing capability as Java
as a programming environment can provide. For example, Java can be used to:
Creating a custom function requires a number of discrete
steps:
Custom functions are registered using the Ebase Designer.
Registration requires the following:
The custom function registration is initiated from the tools
menu (Tools --> Maintain functions)

The Java program must take the following form:
|
/** * Class : Repayment * * Function : Provides run time mapping to the Ebase repayment custom function. * */
import java.util.*; import org.nfunk.jep.*; import com.ebasetech.ufs.function.*; import com.ebasetech.ufs.kernel.*;
public class Repayment extends UFSCustomFunction { /** * Constructor */ public Repayment(UFSFormInterface formInterface) {
super(formInterface);
/* specify the number of parameters to be received by this custom function */
numberOfParameters = 4; } /** * Function : Custom function to perform a mortgage repayment recalculation. * * Parameters : loan : amount of loan * intRate : interest rate * years : repayment period * repaymentType : I for Interest Only, R for Repayment * */ public void run(Stack inStack) throws FormException, ParseException {
// check the stack // checkStack(inStack);
// get the parameter from the stack // Object param1 = inStack.pop(); Object param2 = inStack.pop(); Object param3 = inStack.pop(); Object param4 = inStack.pop();
// check whether the argument is of the right type // if ( param1 instanceof String && param2 instanceof Double && param3 instanceof Double && param4 instanceof Double) {
// set up the passed parameters... // double loan = ((Double) param4).doubleValue(); double intRate = ((Double) param3).doubleValue(); double years = ((Double) param2).doubleValue(); String repaymentType = param1.toString();
// perform the function... // double rate = intRate / 100;
double top = loan * rate; double bot;
if (repaymentType.equals("I")) { bot = 1; } else { bot = 1 - 1 / Math.pow(rate + 1, years); }
double amt = (top / bot) / 12; amt = Math.round(amt * 100) / 100.0;
// push the result on the inStack // inStack.push(new Double(amt));
} else { throw new ParseException("Invalid parameter type"); } } }
|
°
|
Notes:
1.
The class must extend the UFSCustomFunction
class.
2.
The number of parameters to be received by the
function must be set in the constructor. Please do not place any additional
code in the class constructor. (See programming
notes)
3.
Parameters are always received via a Stack
object.
4.
Once parameters are retrieved from the stack
their type must be checked. If the type does not match the expected type then
an exception must be thrown.
5.
The result of the function is returned via the
Stack.
At execution time i.e. when FPL finds a custom function, the
Ebase form processor will instantiate the Class mapped to the custom function
and will execute the run() method of that Class.
The form external interface class is made available to the
function class. This can be used to directly access and change form attributes
such as current page, field values etc. (See Ebase External Interface for more
information) This can be used by referring to attribute formInterface
e.g.
String currentPage =
formInterface.getCurrentPage();
Functions should signal errors by throwing either a ParseException
or a FormException. ParseException should be thrown for command syntax
errors and FormException for other runtime problems. Both of these will result
in termination of the form and logging of the error. In addition, if the system
is in test mode, the error message will be displayed on the HTML page and the
execution log. e.g.
if
( errorCondition )
{
throw new FormException("Description of
this particular error");
}
Functions
wishing to return an integer, say int myInt, should return a Double
representing the value. e.g.
inStack.push(new Double(myInt));
Functions
wishing to return a boolean, should return a Double containing the value 0.0
for false and 1.0 for true.
All functions defined to the system are instantiated
each time any form is started. For this reason, it is important that
constructor code is kept to a minimum.
The following Java is an example of a test program for the
Repayment function above. It enables the Java Class which implements the custom
function to be tested before integration with Ebase.
|
/** * Class : RepaymentTest * * Function : Tests the repayment custom function. * */
import org.nfunk.jep.*; import com.ebasetech.ufs.kernel.*; class RepaymentTest { /** * Constructor. */ public RepaymentTest() {} /** * Main method. Test of the repayment custom function. */ public static void main(String args[])throws FormException{
double value;
// create a new parser... // JEP parser = new JEP();
// define an expression to test the repayment function... // String expr = "repayment(10000,25,5,\"R\")";
// add the function class to the expression evaluator... // parser.addFunction("repayment", new com.ebasetech.ufs.customfunctions.Repayment(null));
// now parse the expression... // parser.parseExpression(expr); if (parser.hasError()) { System.out.println("Error while parsing"); System.out.println(parser.getErrorInfo()); return; }
// now retrieve the result from evaluating repayment function... // value = parser.getValue(); if (parser.hasError()) { System.out.println("Error during evaluation"); System.out.println(parser.getErrorInfo()); return; } System.out.println(expr + " = " + parser.getValue()); } } |
|
In order to use a custom function within FPL the definition
of the function must be added to FPL Script
Command Syntax. This is necessary because FPL syntax must be accurately
checked when creating scripts within the Ebase Designer. Language syntax
checking is invoked when the verify button is clicked within the script editor.
Ebase stores the FPL language definition in Extended Bachus
Naur Form (EBNF) and uses JAVACC, a Java compiler compiler from Sun
Microsystems, to generate language syntax checking software (Java).
The process of adding a new function to FPL is as follows:
The FPL language definition is stored in file FPL.JJ, which
is included in the Fpl/com/ebasetech/ufs/parse directory of the Ebase
installation. The FPL.JJ file is a text document.
1. Open the FPL.JJ in a text editor.
2. Add the name of the function to the section below
/***********************************************
* Custom function
token definitions added below
***********************************************
*/
{
< STARTSWITH:
"STARTSWITH">
| < ENDSWITH:
"ENDSWITH">
| < CONTAINS:
"CONTAINS">
| < UPPERCASE:
"UPPERCASE">
| < LOWERCASE:
"LOWERCASE">
| < SUBSTRING:
"SUBSTRING">
| < ROUND:
"ROUND">
| < TOSTRING:
"TOSTRING">
| < DAYOFWEEK:
"DAYOFWEEK">
| < DATETOSTRING:
"DATETOSTRING">
| < REPAYMENT:
"REPAYMENT">
}
3. Also add the function to the following section
/********************************************************
* Add custom
function to the Function() definition below
********************************************************
*/
void Function() :
{}
{ ( StartsWith() | EndsWith()
| Contains() | Uppercase() | Lowercase() | Substring() | Round() |
Tostring() | DayofWeek() |
DateToString() |
Repayment()
)
}
4. Lastly, add the function definition to the following
section
/****************************************
* Custom function
definitions added below
****************************************
*/
void Repayment()
:
{}
{
<REPAYMENT>
<LPAREN> Expression() <COMMA> Expression() <COMMA>
Expression() <COMMA> Expression() <RPAREN>
}
The
repayment function definition is shown as an example. The example represents a
function called repayment, which accepts four comma-delimited parameters, each
of which can be an expression. An expression can be any combination of
literals, constants, field names, operators and other functions.
If your
function has syntactical requirements beyond this example please contact Ebase
Technology Technical Support for assistance.
Once the
FPL.JJ file has been amended, the language syntax checking software must be
generated.
The FPL
Language Parser is generated by executing the JavaCC program against the FPL.JJ
file (see above). Before execution,
JavaCC must be installed on your system.
The Ebase installation directory Fpl contains a zip
file, JavaCC2_1.zip, that contains the JavaCC2_1.class.
Extract the JavaCC2_1.class from the zip file and run it as
you would any other Java class e.g.:
>>
java JavaCC2_1
Notes:
a) You should not type the .class at the end.
b) Ensure that your environment variable PATH includes
java_install_dir/bin, where java_install_dir is the folder where Java is
installed.
(e.g. set PATH=%PATH%;.../java_install_dir/bin)
This will lead you through a sequence of screens that result
in the installation of JavaCC on your machine.
Modify your environment variable, PATH, to include
javacc_install_dir/bin, where javacc_install_dir is the folder where JavaCC was
installed.
(e.g. set PATH=%PATH%;.../javacc_install_dir/bin)
Modify your environment variable, CLASSPATH, to include
<FPLdir>.
(e.g. set CLASSPATH=%CLASSPATH%;.../UFS/Fpl)
Once installed the JavaCC program can be executed against the
FPL.JJ file:
For
example, from dos
>>
cd
<FPLdir>com\ebasetech\ufs\parse
>>
javacc FPL.jj
Syntactic
errors will be reported by JavaCC. When the execution completes without errors
the current directory will contain 7 generated Java source files.
Now compile
FPLParser Java source file. This will generate 8 class files.
>>
javac FPLParser.java
If the
CLASSPATH has not been set correctly to include <FPLdir>, this step will
report numerous errors.
Testing
the Parser
At this stage,
the new syntax definitions may be tested as follows (this step is recommended
but not required) :
· Run the
syntax checker and when prompted, type FPL statements
>>
cd <FPLdir>
>>
java
com/ebasetech/ufs/parse/FPLParser
· Type any
valid command followed by a semicolon. e.g.
>>
set x = mynewfunction(a,b);
If the syntax is valid, no output is produced and you can enter another
command.
If invalid, you will get a response such as:
Encountered .... ," at line 1, column 12.
Was expecting one of:
...
...
and the program terminates.
Creation
of the UFSLanguage.jar file
>> cd <FPLdir>
>> jar cf UFSLanguage.jar
com/ebasetech/ufs/parse/*.class
This will
create the UFSLanguage.jar file in the <FPLdir> folder.
The
UFSLanguage jar file is required by the designer only. It's location will vary
according to the installation. Typically, it will be in folder
.../UfsClient/lib. Copy the new file to the designer.
On the
server, add jar files to directory .../ufs/WEB-INF/lib and class files to
directory .../ufs/WEB-INF/classes. Jar files and classes added to these
directories are automatically added to the Ebase web application classpath. If
classes are added, the directory structure beneath the classes directory must
match the full package name of the Java class exactly, respecting case. e.g.
class abc.xyz.MyClass.class would result in the structure
/WEB-INF/classes/abc/xyz/MyClass.class.