/* ====================================================================
* Limited Evaluation License:
*
* This software is open source, but licensed. The license with this package
* is an evaluation license, which may not be used for productive systems. If
* you want a full license, please contact us.
*
* The exclusive owner of this work is the OpenRate project.
* This work, including all associated documents and components
* is Copyright of the OpenRate project 2006-2015.
*
* The following restrictions apply unless they are expressly relaxed in a
* contractual agreement between the license holder or one of its officially
* assigned agents and you or your organisation:
*
* 1) This work may not be disclosed, either in full or in part, in any form
* electronic or physical, to any third party. This includes both in the
* form of source code and compiled modules.
* 2) This work contains trade secrets in the form of architecture, algorithms
* methods and technologies. These trade secrets may not be disclosed to
* third parties in any form, either directly or in summary or paraphrased
* form, nor may these trade secrets be used to construct products of a
* similar or competing nature either by you or third parties.
* 3) This work may not be included in full or in part in any application.
* 4) You may not remove or alter any proprietary legends or notices contained
* in or on this work.
* 5) This software may not be reverse-engineered or otherwise decompiled, if
* you received this work in a compiled form.
* 6) This work is licensed, not sold. Possession of this software does not
* imply or grant any right to you.
* 7) You agree to disclose any changes to this work to the copyright holder
* and that the copyright holder may include any such changes at its own
* discretion into the work
* 8) You agree not to derive other works from the trade secrets in this work,
* and that any such derivation may make you liable to pay damages to the
* copyright holder
* 9) You agree to use this software exclusively for evaluation purposes, and
* that you shall not use this software to derive commercial profit or
* support your business or personal activities.
*
* This software is provided "as is" and any expressed or impled warranties,
* including, but not limited to, the impled warranties of merchantability
* and fitness for a particular purpose are disclaimed. In no event shall
* The OpenRate Project or its officially assigned agents be liable to any
* direct, indirect, incidental, special, exemplary, or consequential damages
* (including but not limited to, procurement of substitute goods or services;
* Loss of use, data, or profits; or any business interruption) however caused
* and on theory of liability, whether in contract, strict liability, or tort
* (including negligence or otherwise) arising in any way out of the use of
* this software, even if advised of the possibility of such damage.
* This software contains portions by The Apache Software Foundation, Robert
* Half International.
* ====================================================================
*/
package OpenRate.cache;
import OpenRate.OpenRate;
import OpenRate.configurationmanager.ClientManager;
import OpenRate.db.DBUtil;
import OpenRate.exception.InitializationException;
import OpenRate.logging.LogUtil;
import OpenRate.utils.PropertyUtils;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* Please <a target='new' href='http://www.open-rate.com/wiki/index.php?title=Regex_Match_Cache'>click here</a> to go to wiki page.
* <br>
* <p>
* This class implements an IRule equivalent that can evaluate a defined regular
* expression pattern. For simplicity, the number of columns in the match
* pattern will be read from the first row of the data that is read from the
* input source. Thereafter, all rows must have the same form, which will be
* defined as the form factor of the data.
*/
public class RegexMatchCache
extends AbstractSyncLoaderCache {
/**
* The default return when there is no match
*/
public static final String NO_REGEX_MATCH = "NOMATCH";
// this is the form factor of the data (the number of columns to read
private int KeyFormFactor = 0;
// List of Services that this Client supports
private final static String SERVICE_OBJECT_COUNT = "ObjectCount";
private final static String SERVICE_GROUP_COUNT = "GroupCount";
private final static String SERVICE_DUMP_MAP = "DumpMap";
/* The SearchMap is the regular map that we will have to search through. This
* is a single entry that is grouped into a search group. The match value is
* one of [RegularExpression|Numerical|RegexExclude], driven by the match type.
*
* The
*/
private class SearchMap {
// Depending on the type, we do a real regex, or a comparison
// 0 = regex
// 1 = "="
// 2 = ">"
// 3 = "<"
// 4 = ">="
// 5 = "<="
// 6 = regex EXCLUDE
int[] matchType;
// We can match this if the type > 0
double[] matchValue;
// Or this if we are dealing with a real regex
Pattern[] matchPattern;
// The results list
ArrayList<String> Results = null;
}
/* The SearchGroup is the collection of search maps that will be searched
* during the evaluation.
*/
private class SearchGroup {
ArrayList<SearchMap> SearchGroup;
}
/**
* The internal cache is organised as a hash of the regex map groups that have
* been defined, each of which holds a variable number of entries to search
* through. This is therefore the index to the group entries.
*/
private final HashMap<String, SearchGroup> GroupCache;
/**
* Constructor
*
* Creates a new instance of the Regex Map Group Cache. The Cache
* contains all of the groups that are known to the module. We estimate the
* size of the hash to some value that is reasonable.
*/
public RegexMatchCache() {
super();
GroupCache = new HashMap<>(200);
}
// -----------------------------------------------------------------------------
// ----------------- Start of overridable Plug In functions --------------------
// -----------------------------------------------------------------------------
/**
* The method allows the implementation class the possibility to manipulate or
* validate the search map fields before they are stored
*
* @param inputSearchMap The search map to validate or manipulate
* @return the modified or checked search map
* @throws InitializationException
*/
public String[] validateSearchMap(String[] inputSearchMap) throws InitializationException {
// Pass through - override to change
return inputSearchMap;
}
/**
* The method allows the implementation class the possibility to validate or
* manipulate the array list return list
*
* @param inputResultList The result list to validate or manipulate
* @return the modified or checked result list
* @throws InitializationException
*/
public ArrayList<String> validateResultList(ArrayList<String> inputResultList) throws InitializationException {
// Pass through - override to change
return inputResultList;
}
// -----------------------------------------------------------------------------
// ------------------ Start of inherited Plug In functions ---------------------
// -----------------------------------------------------------------------------
/**
* loadCache is called automatically on startup of the cache factory, as a
* result of implementing the CacheLoader interface. This should be used to
* load any data that needs loading, and to set up variables.
*
* @throws InitializationException
*/
@Override
public void loadCache(String ResourceName, String CacheName)
throws InitializationException {
// Variable definitions
String tmpValue;
// Get the number of key fields (the rest are treated as results)
tmpValue = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName,
CacheName,
"KeyFields",
"None");
if (tmpValue.equals("None")) {
// We will use calculate the default assuming 1 result field
KeyFormFactor = 0;
} else {
try {
KeyFormFactor = Integer.parseInt(tmpValue);
} catch (NumberFormatException nfe) {
message = "KeyFields entry for cache <" + getSymbolicName()
+ "> not numeric. Found value <" + tmpValue + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message, getSymbolicName());
}
}
// Now perform the base initialisation
super.loadCache(ResourceName, CacheName);
}
/**
* Add a value into the Regex Map Cache, defining the result value that should
* be returned in the case of a match. The order of evaluation of the items in
* the group is the order that they are defined, but it would be a simple task
* to order them by some value after loading.
*
* @param Group The Regex group to add this pattern to
* @param fields The list of fields to add to the group
* @param ResultList The list of result fields to add
* @throws OpenRate.exception.InitializationException
*/
private void addEntry(String Group, String[] fields, ArrayList<String> ResultList)
throws InitializationException {
int i;
SearchMap tmpSearchMap;
SearchGroup tmpSearchGroup;
String Helper;
String FirstChar;
String SecondChar;
String valueToParse;
String[] checkedFields;
ArrayList<String> checkedResultList;
// Allow the user to check the input fields
checkedFields = validateSearchMap(fields);
// Allow the user to check the return fields
checkedResultList = validateResultList(ResultList);
// See if we already know this group, if not add it
if (GroupCache.containsKey(Group)) {
// Get the existing value
tmpSearchGroup = GroupCache.get(Group);
} else {
// We don't know it, so add it
tmpSearchGroup = new SearchGroup();
tmpSearchGroup.SearchGroup = new ArrayList<>();
GroupCache.put(Group, tmpSearchGroup);
}
// Create the new search Object.
tmpSearchMap = new SearchMap();
// Compile and add the search map
tmpSearchMap.matchPattern = new Pattern[checkedFields.length];
tmpSearchMap.matchType = new int[checkedFields.length];
tmpSearchMap.matchValue = new double[checkedFields.length];
for (i = 0; i < fields.length; i++) {
// get the short version of the string for understanding what it is
Helper = fields[i].replaceAll(" ", "") + " ";
FirstChar = Helper.substring(0, 1);
SecondChar = Helper.substring(1, 2);
if ((FirstChar.equals("<"))
| (FirstChar.equals(">"))
| (FirstChar.equals("="))) {
// try to parse for simple numerical comparison
if (FirstChar.equals("=")) {
tmpSearchMap.matchType[i] = 1;
valueToParse = Helper.substring(1).trim();
try {
tmpSearchMap.matchValue[i] = Double.parseDouble(valueToParse);
} catch (NumberFormatException ex) {
throw new InitializationException("Could not parse value <" + valueToParse + "> as a double value", getSymbolicName());
}
continue;
}
if (FirstChar.equals(">")) {
if (SecondChar.equals("=")) {
tmpSearchMap.matchType[i] = 4;
valueToParse = Helper.substring(2).trim();
try {
tmpSearchMap.matchValue[i] = Double.parseDouble(valueToParse);
} catch (NumberFormatException ex) {
throw new InitializationException("Could not parse value <" + valueToParse + "> as a double value", getSymbolicName());
}
continue;
} else {
// we got this far, must be just ">"
tmpSearchMap.matchType[i] = 2;
valueToParse = Helper.substring(1).trim();
try {
tmpSearchMap.matchValue[i] = Double.parseDouble(valueToParse);
} catch (NumberFormatException ex) {
throw new InitializationException("Could not parse value <" + valueToParse + "> as a double value", getSymbolicName());
}
continue;
}
}
if (FirstChar.equals("<")) {
if (SecondChar.equals("=")) {
tmpSearchMap.matchType[i] = 5;
valueToParse = Helper.substring(2).trim();
try {
tmpSearchMap.matchValue[i] = Double.parseDouble(valueToParse);
} catch (NumberFormatException ex) {
throw new InitializationException("Could not parse value <" + valueToParse + "> as a double value", getSymbolicName());
}
} else {
// we got this far, must be just "<"
tmpSearchMap.matchType[i] = 3;
valueToParse = Helper.substring(1).trim();
try {
tmpSearchMap.matchValue[i] = Double.parseDouble(valueToParse);
} catch (NumberFormatException ex) {
throw new InitializationException("Could not parse value <" + valueToParse + "> as a double value", getSymbolicName());
}
}
}
} else {
if (FirstChar.equals("!")) {
// This is a regex negation, remove the ! and set the flag and regex
// for the rest
tmpSearchMap.matchPattern[i] = Pattern.compile(fields[i].substring(1));
tmpSearchMap.matchType[i] = 6;
} else {
// if we got this far it is Real Regex inclusion
try {
tmpSearchMap.matchPattern[i] = Pattern.compile(fields[i]);
} catch (PatternSyntaxException pse) {
message = "Error compiling regex pattern <" + fields[i]
+ "> in module <" + getSymbolicName() + ">. message <" + pse.getMessage() + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message, getSymbolicName());
}
tmpSearchMap.matchType[i] = 0;
}
}
}
tmpSearchMap.Results = checkedResultList;
tmpSearchGroup.SearchGroup.add(tmpSearchMap);
}
/**
* Evaluate an input against the search group. This is the generalised from
* which you may want to create specialised versions for a defined number of
* parameters, for reasons of performance.
*
* This function returns only the first match. If you want to get all matching
* entries, this is done using getAllEntries()
*
* @param Group The Regex group to search
* @param Parameters The list of fields to search
* @return Result The result of the search
*/
public String getMatch(String Group, String[] Parameters) {
SearchMap tmpSearchResult;
tmpSearchResult = getMatchingSearchResult(Group, Parameters);
if (tmpSearchResult == null) {
return NO_REGEX_MATCH;
} else {
return tmpSearchResult.Results.get(0);
}
}
/**
* Evaluate an input against the search group. This is the generalised from
* which you may want to create specialised versions for a defined number of
* parameters, for reasons of performance.
*
* This function returns only the first match. If you want to get all matching
* entries, this is done using getAllEntries()
*
* @param Group The Regex group to search
* @param Parameters The list of fields to search
* @return Result The result of the search as a vector of strings
*/
public ArrayList<String> getMatchWithChildData(String Group, String[] Parameters) {
SearchMap tmpSearchResult;
ArrayList<String> tmpResult;
tmpSearchResult = getMatchingSearchResult(Group, Parameters);
if (tmpSearchResult == null) {
tmpResult = new ArrayList<>();
tmpResult.add(NO_REGEX_MATCH);
return tmpResult;
} else {
return tmpSearchResult.Results;
}
}
/**
* Evaluate an input against the search group. This is the generalised from
* which you may want to create specialised versions for a defined number of
* parameters, for reasons of performance.
*
* This function returns only the first match.
*
* @param Group The Regular expression group to search
* @param Parameters The list of fields to search
* @return Result The result of the search as a SearchMap object
*/
private SearchMap getMatchingSearchResult(String Group, String[] Parameters) {
int i;
SearchGroup tmpSearchGroup;
SearchMap tmpSearchMap;
Pattern tmpPattern;
boolean Found;
double tmpParamValue;
// recover the object
tmpSearchGroup = GroupCache.get(Group);
if (tmpSearchGroup == null) {
// Return a default value
return null;
} else {
// Iterate thorough the entries in the group
Iterator<SearchMap> GroupIter = tmpSearchGroup.SearchGroup.listIterator();
while (GroupIter.hasNext()) {
tmpSearchMap = GroupIter.next();
// Initialise the found flag and the counter
Found = true;
i = 0;
// Now check the elements of the map
while ((i < Parameters.length) & Found) {
switch (tmpSearchMap.matchType[i]) {
// Regex inclusion case
case 0: {
tmpPattern = tmpSearchMap.matchPattern[i];
if (Parameters[i] == null) {
// we cannot match on null values - warn once and out...
OpenRate.getOpenRateFrameworkLog().warning("Null value found in regex match on parameter <" + i + "> in module <" + getSymbolicName() + ">");
return null;
}
if (!tmpPattern.matcher(Parameters[i]).matches()) {
// We did not get a match, move on
Found = false;
}
break;
}
// Regex exclusion case
case 6: {
tmpPattern = tmpSearchMap.matchPattern[i];
if (tmpPattern.matcher(Parameters[i]).matches()) {
// We did not get a match, move on
Found = false;
}
break;
}
// "=" case
case 1: {
tmpParamValue = Double.parseDouble(Parameters[i]);
if (tmpSearchMap.matchValue[i] != tmpParamValue) {
// We did not get a match, move on
Found = false;
}
break;
}
// ">" case
case 2: {
tmpParamValue = Double.parseDouble(Parameters[i]);
if (tmpParamValue <= tmpSearchMap.matchValue[i]) {
// We did not get a match, move on
Found = false;
}
break;
}
// "<" case
case 3: {
tmpParamValue = Double.parseDouble(Parameters[i]);
if (tmpParamValue >= tmpSearchMap.matchValue[i]) {
// We did not get a match, move on
Found = false;
}
break;
}
// ">=" case
case 4: {
tmpParamValue = Double.parseDouble(Parameters[i]);
if (tmpParamValue < tmpSearchMap.matchValue[i]) {
// We did not get a match, move on
Found = false;
}
break;
}
// "<=" case
case 5: {
tmpParamValue = Double.parseDouble(Parameters[i]);
if (tmpParamValue > tmpSearchMap.matchValue[i]) {
// We did not get a match, move on
Found = false;
}
break;
}
}
// Increment the loop counter
i++;
}
if (Found) {
return tmpSearchMap;
}
}
// Return a default value - we found nothing
return null;
}
}
/**
* Evaluate an input against the search group. This is the generalised from
* which you may want to create specialised versions for a defined number of
* parameters, for reasons of performance.
*
* This function returns all of the entries that are matched, in priority
* order. This is useful for aggregation processing etc.
*
* @param Group The Regex group to search
* @param Parameters The list of fields to search
* @return List of all matches
*/
public ArrayList<String> getAllEntries(String Group, String[] Parameters) {
int i;
SearchGroup tmpSearchGroup;
SearchMap tmpSearchMap;
Pattern tmpPattern;
boolean found;
double tmpParamValue;
ArrayList<String> matches;
matches = new ArrayList<>();
// recover the object
tmpSearchGroup = GroupCache.get(Group);
if (tmpSearchGroup == null) {
// Return a default value, we did not find the group
return matches;
} else {
// Iterate thorough the entries in the group
Iterator<SearchMap> GroupIter = tmpSearchGroup.SearchGroup.listIterator();
while (GroupIter.hasNext()) {
tmpSearchMap = GroupIter.next();
// Initialise the found flag and the counter
found = true;
i = 0;
// Now check the elements of the map
while ((i < Parameters.length) & found) {
switch (tmpSearchMap.matchType[i]) {
// Regex inclusion case
case 0: {
tmpPattern = tmpSearchMap.matchPattern[i];
if (!tmpPattern.matcher(Parameters[i]).matches()) {
// We did not get a match, move on
found = false;
}
break;
}
// Regex exclusion case
case 6: {
tmpPattern = tmpSearchMap.matchPattern[i];
if (tmpPattern.matcher(Parameters[i]).matches()) {
// We did not get a match, move on
found = false;
}
break;
}
// "=" case
case 1: {
tmpParamValue = Double.parseDouble(Parameters[i]);
if (tmpSearchMap.matchValue[i] != tmpParamValue) {
// We did not get a match, move on
found = false;
}
break;
}
// ">" case
case 2: {
tmpParamValue = Double.parseDouble(Parameters[i]);
if (tmpParamValue <= tmpSearchMap.matchValue[i]) {
// We did not get a match, move on
found = false;
}
break;
}
// "<" case
case 3: {
tmpParamValue = Double.parseDouble(Parameters[i]);
if (tmpParamValue >= tmpSearchMap.matchValue[i]) {
// We did not get a match, move on
found = false;
}
break;
}
// ">=" case
case 4: {
tmpParamValue = Double.parseDouble(Parameters[i]);
if (tmpParamValue < tmpSearchMap.matchValue[i]) {
// We did not get a match, move on
found = false;
}
break;
}
// "<=" case
case 5: {
tmpParamValue = Double.parseDouble(Parameters[i]);
if (tmpParamValue > tmpSearchMap.matchValue[i]) {
// We did not get a match, move on
found = false;
}
break;
}
}
// Increment the loop counter
i++;
}
if (found) {
matches.add(tmpSearchMap.Results.get(0));
}
}
return matches;
}
}
/**
* Load the data from the defined file
*
* @throws OpenRate.exception.InitializationException
*/
@Override
public void loadDataFromFile()
throws InitializationException {
// Variable declarations
BufferedReader inFile;
int MapsLoaded = 0;
String[] MapEntryFields;
String[] SearchMapFields;
String tmpFileRecord;
String Group;
int ColumnCount;
int ColumnIdx;
int ResultFormFactor = 0;
ArrayList<String> tmpResultList;
// Find the location of the configuration file
OpenRate.getOpenRateFrameworkLog().info("Starting Regex Match Cache Loading from file for <" + getSymbolicName() + ">");
// Try to open the file
try {
inFile = new BufferedReader(new FileReader(cacheDataFile));
} catch (FileNotFoundException ex) {
message = "Application is not able to read file : <"
+ cacheDataFile + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message, ex, getSymbolicName());
}
// File open, now get the stuff
try {
while (inFile.ready()) {
tmpFileRecord = inFile.readLine();
if ((tmpFileRecord.startsWith("#"))
| tmpFileRecord.trim().equals("")) {
// comment do not load
} else {
MapsLoaded++;
MapEntryFields = tmpFileRecord.split(";");
ColumnCount = MapEntryFields.length;
// Read the form factor from the first entry, and then make sure
// that we always get this
if (ResultFormFactor == 0) {
// Check that this is a form factor we are able to use. There
// must be at least 3 fields for us to be able to work with
// the record:
// - Group identifier
// - Some fields to compare (min 1)
// - a result
if (ColumnCount < 3) {
// we're not going to be able to use this
message = "You must define at least 3 entries in the record, you have defined <"
+ ColumnCount + "> in module <" + getSymbolicName() + ">";
OpenRate.getOpenRateFrameworkLog().fatal(message);
throw new InitializationException(message, getSymbolicName());
}
// Do we have a defined form factor?
if (KeyFormFactor == 0) {
// If we have not been given a key form factor, assume the result factor is 1
ResultFormFactor = 1;
KeyFormFactor = ColumnCount - ResultFormFactor - 1;
message = "Using default key factor for regex cache <"
+ cacheDataFile + ">. Assuming Key = <"
+ Integer.toString(KeyFormFactor) + "> and Result = <"
+ Integer.toString(ResultFormFactor)
+ "> in module <" + getSymbolicName() + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
} else {
// Use the given factor
ResultFormFactor = ColumnCount - KeyFormFactor - 1;
message = "Using defined key factor for regex cache <"
+ cacheDataFile + ">. Using Key = <"
+ Integer.toString(KeyFormFactor) + "> and Result = <"
+ Integer.toString(ResultFormFactor) + "> in module <"
+ getSymbolicName() + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
}
}
// Check that we are always getting the same form factor
if (MapEntryFields.length == (KeyFormFactor + ResultFormFactor + 1)) {
//Add the row, after extracting the group and the result
Group = MapEntryFields[0];
SearchMapFields = new String[KeyFormFactor];
for (ColumnIdx = 0; ColumnIdx < KeyFormFactor; ColumnIdx++) {
SearchMapFields[ColumnIdx] = MapEntryFields[ColumnIdx + 1];
}
tmpResultList = new ArrayList<>();
for (ColumnIdx = KeyFormFactor; ColumnIdx < (ResultFormFactor + KeyFormFactor); ColumnIdx++) {
tmpResultList.add(MapEntryFields[ColumnIdx + 1]);
}
// Add the map
addEntry(Group.intern(), SearchMapFields, tmpResultList);
} else {
// Error because the form factor changed
message = "Form Factor should be Key = <" + Integer.toString(KeyFormFactor)
+ "> + Payload = <" + Integer.toString(ResultFormFactor)
+ ">, but received <" + MapEntryFields.length + "> in module <" + getSymbolicName() + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message, getSymbolicName());
}
// Update to the log file
if ((MapsLoaded % loadingLogNotificationStep) == 0) {
message = "Regex Map Data Loading: <" + MapsLoaded
+ "> configurations loaded for <" + getSymbolicName() + "> from <"
+ cacheDataFile + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
}
}
}
} catch (IOException ex) {
message = "Error reading input file <" + cacheDataFile
+ "> in record <" + MapsLoaded + ">. IO Error.";
OpenRate.getOpenRateFrameworkLog().fatal(message);
} catch (ArrayIndexOutOfBoundsException ex) {
message = "Error reading input file <" + cacheDataFile
+ "> in record <" + MapsLoaded + ">. Malformed Record.";
OpenRate.getOpenRateFrameworkLog().fatal(message);
} catch (NullPointerException npe) {
message = "Error reading input file <" + cacheDataFile
+ "> in record <" + MapsLoaded + ">. Malformed Record in module <" + getSymbolicName() + ">";
OpenRate.getOpenRateFrameworkLog().fatal(message);
} finally {
try {
inFile.close();
} catch (IOException ex) {
message = "Error closing input file <" + cacheDataFile
+ "> in module <" + getSymbolicName() + ">";
OpenRate.getOpenRateFrameworkLog().error(message, ex);
}
}
message = "Regex Map Data Loading completed. <" + MapsLoaded
+ "> configuration lines loaded for <" + getSymbolicName() + "> from <"
+ cacheDataFile + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
}
/**
* Load the data from the defined Data Source
*
* @throws OpenRate.exception.InitializationException
*/
@Override
public void loadDataFromDB()
throws InitializationException {
int ColumnIdx;
int ColumnCount;
int ConfigsLoaded = 0;
String Group;
ResultSetMetaData Rsmd;
String[] SearchMapFields;
int ResultFormFactor = 0;
ArrayList<String> tmpResultList;
// Find the location of the zone configuration file
OpenRate.getOpenRateFrameworkLog().info("Starting Regex Match Cache Loading from DB for <" + getSymbolicName() + ">");
// Try to open the DS
JDBCcon = DBUtil.getConnection(cacheDataSourceName);
// Now prepare the statements
prepareStatements();
// Execute the query
try {
mrs = StmtCacheDataSelectQuery.executeQuery();
} catch (SQLException ex) {
message = "Error performing SQL for retrieving Regex Match data in module <" + getSymbolicName() + ">. message <" + ex.getMessage() + ">";
OpenRate.getOpenRateFrameworkLog().fatal(message);
throw new InitializationException(message, getSymbolicName());
}
// loop through the results for the customer login cache
try {
Rsmd = mrs.getMetaData();
ColumnCount = Rsmd.getColumnCount();
// Read the form factor from the first entry, and then make sure
// that we always get this
if (ResultFormFactor == 0) {
// Check that this is a form factor we are able to use. There
// must be at least 3 fields for us to be able to work with
// the record:
// - Group identifier
// - Some fields to compare (min 1)
// - a result
if (ColumnCount < 3) {
// we're not going to be able to use this
message = "You must define at least 3 entries in the record, you have defined <"
+ ColumnCount + ">";
OpenRate.getOpenRateFrameworkLog().fatal(message);
throw new InitializationException(message, getSymbolicName());
}
// Do we have a defined form factor?
if (KeyFormFactor == 0) {
// If we have not been given a key form factor, assume the result factor is 1
ResultFormFactor = 1;
KeyFormFactor = ColumnCount - ResultFormFactor - 1;
message = "Using default key factor for regex cache <"
+ cacheDataSourceName + ">. Assuming Key = <"
+ Integer.toString(KeyFormFactor) + "> and Result = <"
+ Integer.toString(ResultFormFactor) + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
} else {
// Use the given factor
ResultFormFactor = ColumnCount - KeyFormFactor - 1;
if (ResultFormFactor < 1) {
// Makes no sense to start if we don't give any results
message = "Error in module <"
+ cacheDataSourceName + ">. Key fields >= total columns. Got KeyFields <"
+ KeyFormFactor + "> and columns <" + ColumnCount + ">";
OpenRate.getOpenRateFrameworkLog().fatal(message);
throw new InitializationException(message, getSymbolicName());
}
message = "Using defined key factor for regex cache <"
+ getSymbolicName() + ">. Using Key = <"
+ Integer.toString(KeyFormFactor) + "> and Result = <"
+ Integer.toString(ResultFormFactor) + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
}
}
// Start the loading
mrs.beforeFirst();
while (mrs.next()) {
ConfigsLoaded++;
Group = mrs.getString(1);
SearchMapFields = new String[KeyFormFactor];
// create the array to transfer the columns into the map
for (ColumnIdx = 1; ColumnIdx < KeyFormFactor + 1; ColumnIdx++) {
SearchMapFields[ColumnIdx - 1] = mrs.getString(ColumnIdx + 1);
}
tmpResultList = new ArrayList<>();
for (ColumnIdx = KeyFormFactor + 1; ColumnIdx < (ResultFormFactor + KeyFormFactor + 1); ColumnIdx++) {
tmpResultList.add(mrs.getString(ColumnIdx + 1));
}
// Add the map
addEntry(Group.intern(), SearchMapFields, tmpResultList);
// Update to the log file
if ((ConfigsLoaded % loadingLogNotificationStep) == 0) {
message = "Regex Map Data Loading: <" + ConfigsLoaded
+ "> configurations loaded for <" + getSymbolicName() + "> from <"
+ cacheDataSourceName + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
}
}
} catch (SQLException ex) {
message = "Error opening Search Map Data for <" + getSymbolicName() + ">";
OpenRate.getOpenRateFrameworkLog().fatal(message);
throw new InitializationException(message, ex, getSymbolicName());
} catch (NullPointerException ex) {
message = "Error opening Search Map Data for <" + getSymbolicName() + "> in config <" + ConfigsLoaded + ">";
OpenRate.getOpenRateFrameworkLog().fatal(message);
throw new InitializationException(message, ex, getSymbolicName());
}
// Close down stuff
try {
mrs.close();
StmtCacheDataSelectQuery.close();
JDBCcon.close();
} catch (SQLException ex) {
message = "Error closing Search Map Data connection for <"
+ cacheDataSourceName + ">";
OpenRate.getOpenRateFrameworkLog().fatal(message);
throw new InitializationException(message, getSymbolicName());
}
message = "Regex Map Data Loading completed. <" + ConfigsLoaded
+ "> configuration lines loaded for <" + getSymbolicName() + "> from <"
+ cacheDataSourceName + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
}
/**
* Load the data from the defined Data Source Method
*
* @throws OpenRate.exception.InitializationException
*/
@Override
public void loadDataFromMethod()
throws InitializationException {
int ColumnIdx;
int ColumnCount;
int ConfigsLoaded = 0;
String Group;
String[] SearchMapFields;
int ResultFormFactor = 0;
ArrayList<String> tmpMethodResult;
ArrayList<String> tmpResultList;
// Find the location of the zone configuration file
OpenRate.getOpenRateFrameworkLog().info("Starting Regex Match Cache Loading from Method for <" + getSymbolicName() + ">");
// Execute the user domain method
Collection<ArrayList<String>> methodLoadResultSet;
methodLoadResultSet = getMethodData(getSymbolicName(), CacheMethodName);
if (methodLoadResultSet == null) {
OpenRate.getOpenRateFrameworkLog().warning("No cache data returned by method <" + CacheMethodName
+ "> in cache <" + getSymbolicName() + ">");
} else {
Iterator<ArrayList<String>> methodDataToLoadIterator = methodLoadResultSet.iterator();
while (methodDataToLoadIterator.hasNext()) {
tmpMethodResult = methodDataToLoadIterator.next();
ConfigsLoaded++;
ColumnCount = tmpMethodResult.size();
// Read the form factor from the first entry, and then make sure
// that we always get this
if (ResultFormFactor == 0) {
// Check that this is a form factor we are able to use. There
// must be at least 3 fields for us to be able to work with
// the record:
// - Group identifier
// - Some fields to compare (min 1)
// - a result
if (ColumnCount < 3) {
// we're not going to be able to use this
message = "You must define at least 3 entries in the record, you have defined <"
+ ColumnCount + ">";
OpenRate.getOpenRateFrameworkLog().fatal(message);
throw new InitializationException(message, getSymbolicName());
}
// Do we have a defined form factor?
if (KeyFormFactor == 0) {
// If we have not been given a key form factor, assume the result factor is 1
ResultFormFactor = 1;
KeyFormFactor = ColumnCount - ResultFormFactor - 1;
message = "Using default key factor for regex cache <"
+ cacheDataSourceName + ">. Assuming Key = <"
+ Integer.toString(KeyFormFactor) + "> and Result = <"
+ Integer.toString(ResultFormFactor) + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
} else {
// Use the given factor
ResultFormFactor = ColumnCount - KeyFormFactor - 1;
message = "Using defined key factor for regex cache <"
+ cacheDataSourceName + ">. Using Key = <"
+ Integer.toString(KeyFormFactor) + "> and Result = <"
+ Integer.toString(ResultFormFactor) + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
}
}
// Check that we are always getting the same form factor
if (ColumnCount == (KeyFormFactor + ResultFormFactor + 1)) {
//Add the row, after extracting the group and the result
Group = tmpMethodResult.get(0);
// Get the search map fields
SearchMapFields = new String[KeyFormFactor];
for (ColumnIdx = 0; ColumnIdx < KeyFormFactor; ColumnIdx++) {
SearchMapFields[ColumnIdx] = tmpMethodResult.get(ColumnIdx + 1);
}
tmpResultList = new ArrayList<>();
for (ColumnIdx = KeyFormFactor; ColumnIdx < (ResultFormFactor + KeyFormFactor); ColumnIdx++) {
tmpResultList.add(tmpMethodResult.get(ColumnIdx + 1));
}
// Add the map
addEntry(Group.intern(), SearchMapFields, tmpResultList);
// Update to the log file
if ((ConfigsLoaded % loadingLogNotificationStep) == 0) {
message = "Regex Map Data Loading: <" + ConfigsLoaded
+ "> configurations loaded for <" + getSymbolicName() + "> from <"
+ cacheDataSourceName + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
}
} else {
// Error because the form factor changed
message = "Form Factor should be Key = <" + Integer.toString(KeyFormFactor)
+ "> + Payload = <" + Integer.toString(ResultFormFactor)
+ ">, but received <" + tmpMethodResult.size() + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message, getSymbolicName());
}
}
}
message = "Regex Map Data Loading completed. " + ConfigsLoaded
+ " configuration lines loaded for <" + getSymbolicName() + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
}
/**
* Dumps the entire contents of the cache to the Log.
*/
protected void DumpMapData() {
String Helper;
Iterator<String> GroupIter;
Iterator<SearchMap> PatternIterator;
Iterator<String> CDIterator;
SearchGroup tmpSearchGroup;
SearchMap tmpSearchMap;
String PrintHelper;
int counter = 0;
OpenRate.getOpenRateFrameworkLog().info("Dumping Map Data for RegexMatchCache <" + getSymbolicName() + ">");
OpenRate.getOpenRateFrameworkLog().info("Groups:");
// Iterate thorough the entries in the group
GroupIter = GroupCache.keySet().iterator();
while (GroupIter.hasNext()) {
Helper = GroupIter.next();
OpenRate.getOpenRateFrameworkLog().info(" " + Helper);
}
// Now dump the data
GroupIter = GroupCache.keySet().iterator();
while (GroupIter.hasNext()) {
Helper = GroupIter.next();
OpenRate.getOpenRateFrameworkLog().info("Dumping group map data for <" + Helper + ">");
tmpSearchGroup = GroupCache.get(Helper);
PatternIterator = tmpSearchGroup.SearchGroup.iterator();
while (PatternIterator.hasNext()) {
OpenRate.getOpenRateFrameworkLog().info("===ENTRY " + counter + "===");
counter++;
PrintHelper = " (";
tmpSearchMap = PatternIterator.next();
for (int i = 0; i < tmpSearchMap.matchPattern.length; i++) {
PrintHelper = PrintHelper + "["
+ tmpSearchMap.matchType[i] + ":"
+ tmpSearchMap.matchPattern[i] + ":"
+ tmpSearchMap.matchValue[i] + "] ";
}
// dump the result array
PrintHelper += ") --> (";
CDIterator = tmpSearchMap.Results.iterator();
while (CDIterator.hasNext()) {
PrintHelper = PrintHelper + CDIterator.next() + ",";
}
PrintHelper += ")";
OpenRate.getOpenRateFrameworkLog().info(PrintHelper);
}
}
}
/**
* Clear down the cache contents in the case that we are ordered to reload
*/
@Override
public void clearCacheObjects() {
GroupCache.clear();
}
// -----------------------------------------------------------------------------
// ------------- Start of inherited IEventInterface functions ------------------
// -----------------------------------------------------------------------------
/**
* registerClientManager registers the client module to the ClientManager
* class which manages all the client modules available in this OpenRate
* Application.
*
* registerClientManager registers this class as a client of the ECI listener
* and publishes the commands that the plug in understands. The listener is
* responsible for delivering only these commands to the plug in.
*
* @throws OpenRate.exception.InitializationException
*
*/
@Override
public void registerClientManager() throws InitializationException {
// Set the client reference and the base services first
super.registerClientManager();
//Register services for this Client
ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_GROUP_COUNT, ClientManager.PARAM_DYNAMIC);
ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_OBJECT_COUNT, ClientManager.PARAM_DYNAMIC);
ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_DUMP_MAP, ClientManager.PARAM_DYNAMIC);
}
/**
* processControlEvent is the method that will be called when an event is
* received for a module that has registered itself as a client of the
* External Control Interface
*
* @param Command - command that is understand by the client module
* @param Init - we are performing initial configuration if true
* @param Parameter - parameter for the command
* @return The result string of the operation
*/
@Override
public String processControlEvent(String Command, boolean Init,
String Parameter) {
SearchGroup tmpSearchGroup;
Collection<String> tmpGroups;
Iterator<String> GroupIter;
String tmpGroupName;
int Objects = 0;
int ResultCode = -1;
// Return the number of objects in the cache
if (Command.equalsIgnoreCase(SERVICE_GROUP_COUNT)) {
return Integer.toString(GroupCache.size());
}
if (Command.equalsIgnoreCase(SERVICE_OBJECT_COUNT)) {
tmpGroups = GroupCache.keySet();
GroupIter = tmpGroups.iterator();
while (GroupIter.hasNext()) {
tmpGroupName = GroupIter.next();
tmpSearchGroup = GroupCache.get(tmpGroupName);
Objects += tmpSearchGroup.SearchGroup.size();
}
return Integer.toString(Objects);
}
// Return the number of objects in the cache
if (Command.equalsIgnoreCase(SERVICE_DUMP_MAP)) {
// onl< dump on a positive command
if (Parameter.equalsIgnoreCase("true")) {
DumpMapData();
}
ResultCode = 0;
}
if (ResultCode == 0) {
OpenRate.getOpenRateFrameworkLog().debug(LogUtil.LogECICacheCommand(getSymbolicName(), Command, Parameter));
return "OK";
} else {
return super.processControlEvent(Command, Init, Parameter);
}
}
}