Sample code for implementing custom rule class
The following code is an example of defining a custom rule class. It shows a very basic rule class implementation that checks new order prices to ensure they do not exceed a defined maximum value. In this example, a new rule class instance can be defined for a single symbol name, and a single price limit. This is only to reduce the complexity of the example code. The rule class is implemented as an event object to show the best practice implementation pattern.
The risk firewall default rule classes follow a similar implementation. See the risk firewall samples in the samples directory of your CMF installation directory.
using com.apama.firewall.RuleClassFactory;
using com.apama.firewall.RuleClass;
using com.apama.firewall.QueryRequest;
using com.apama.firewall.QueryResponse;
using com.apama.utils.Error;
using com.apama.utils.Params;
using com.apama.utils.ParamsSchema;
event MyRiskFirewallRuleClass {
action create() returns RuleClass {
// Construct an instance of the risk firewall rule class interface
// that overrides provided actions to implement custom functionality.
// This example does not override the modifyRule() action because the
// implementation does not support modifying rule instances. This
// example also does not override the deleteRuleInstance() action
// because this implementation does not do any clean up.
RuleClass ruleClassIface :=
(new RuleClassFactory).create( "MyRiskFirewallRuleClass" );
ruleClassIface.addRuleInstance := addRuleInstance;
ruleClassIface.getSchema := getSchema;
ruleClassIface.evaluateRuleInstanceQuery := evaluateRuleInstanceQuery;
return ruleClassIface;
}
action getSchema() returns ParamsSchema {
// Define the configuration schema for this new rule class.
ParamsSchema configSchema := new ParamsSchema;
configSchema.addItemMinimal(
"SLICE_SYMBOL","string","","Symbol set this price limit is for");
configSchema.addItemMinimal(
"PRICE_LIMIT","float","0.0","Maximum price allowed for orders");
return configSchema;
}
// This action is called whenever the Risk Firewall Service needs to
// create a new instance of this custom rule class implementation.
action addRuleInstance( integer instanceId, Params config,
action<> cbOnSuccess,
action< Error > cbOnError ) {
boolean success := false;
Error error := new Error;
// Check that the configuration is valid.
if( config.hasParam( "SLICE_SYMBOL" ) ) then {
if( config.hasParam( "PRICE_LIMIT" ) ) then {
// Required parameters are provided.
success := true;
} else {
// Add some information about missing parameter.
error.params.addParam("MISSING_PARAM", "PRICE_LIMIT");
error.message := "Failed to add Risk Firewall Rule Instance :
"+instanceId.toString()+" :
Missing 'PRICE_LIMIT' configuration value.";
}
} else {
// Add some information about missing parameter.
error.params.addParam("MISSING_PARAM", "SLICE_SYMBOL");
error.message := "Failed to add Risk Firewall Rule Instance :
"+instanceId.toString()+" :
Missing 'SLICE_SYMBOL' configuration value.";
}
// Use a callback to indicate to the risk firewall whether the
// operation was successful.
if( success ) then {
cbOnSuccess();
} else {
cbOnError( error );
}
}
// This action is called whenever the Risk Firewall Service needs to
// query this custom rule class implementation.
action evaluateRuleInstanceQuery(
integer instanceId,
Params config,
QueryRequest query,
action< integer/*instanceId*/ QueryResponse > cbCompleted ) {
// Construct the query result object, default to pass.
QueryResponse queryResponse := new QueryResponse;
// Set the requestId on the response to match it to the request.
queryResponse.requestId := query.requestId;
// Check that this symbol is in the specified slice.
string symbol := query.params.getSymbol();
<string> symbolSlice := sequence<string>.parse(
config.getParam( "SYMBOL_SLICE" ) );
if( symbolSlice.indexOf( symbol ) > 0 ) then {
// Check if the order price exceeds the price defined in
// this rule. If it does, return the query as invalid.
float orderPrice := query.params.getPrice();
float priceLimit := config.getFloatParamOr( "PRICE_LIMIT", 0.0 );
if( orderPrice > priceLimit ) then {
queryResponse.response := QueryConstants.QUERYRESPONSE_FAIL;
queryResponse.message :=
"Price Order Limit exceeded for "+symbol+" - Limit is:
"+priceLimit.toString();
}
}
// Execute callback to indicate to the Risk Firewall Service
// whether the query passed or failed.
cbCompleted( instanceId, queryResponse );
}
}
The following code shows how this custom RuleClass implementation would be registered.
using com.apama.firewall.RiskFirewallFactory;
using com.apama.firewall.RiskFirewall;
monitor RiskFirewallCustomRuleClassExample {
context mainContext := context.current();
action onload() {
// Use the risk firewall factory to create a new risk
// firewall instance called "MyFirewall". In this case,
// use the default configuration parameter settings.
RiskFirewall rfw :=
(new RiskFirewallFactory).create( mainContext, "MyFirewall" );
// Register custom rule class with this risk firewall.
myFirewall.registerRuleClass( (new MyRiskFirewallRuleClass).create() );
// Alternatively, for notification of when registration of the
// rule class is complete, call the following action.
rfw.registerRuleClassCb(
(new MyRiskFirewallRuleClass).create(), cbOnRuleClassRegistered );
}
// This action is called after the rule class is successfully registered.
action cbOnRuleClassRegistered(
RiskFirewall rfw, string ruleClassName ) {
log "Successfully registered RuleClass: "+ruleClassName;
}
}