/* ====================================================================
* 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.configurationmanager.IEventInterface;
import OpenRate.db.DBUtil;
import OpenRate.exception.InitializationException;
import OpenRate.lang.BalanceGroup;
import OpenRate.lang.Counter;
import OpenRate.lang.CounterGroup;
import OpenRate.logging.LogUtil;
import OpenRate.utils.ConversionUtils;
import OpenRate.utils.PropertyUtils;
import java.io.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* All balances start with a value of zero. CDRs increment the balances
* which are then used to calculate bonuses. This is a relatively simple
* module for reading and writing static values.
*
* This cache is designed as a read/write cache, but does not at the moment
* manage transactions. That means if a transaction fails (very unusual)
* either the balances will have to be cleaned up, or reloaded from the table.
*
* The data should be presented in the form:
* BalanceGroupId - the identifier of the balance group (integer)
* CounterId - the counter identifier (integer)
* RecordId - the counter instance identifier (integer)
* Valid From - the start date of the instance (UTC date as long)
* Valid To - the end date of the instance (UTC date as long)
* Current Balance- the current value of the counter (double)
*
* @author i.sparkes
*/
public class BalanceCache extends AbstractCache
implements ICacheLoader,
ICacheSaver,
IEventInterface
{
// This is the source type of the data to load
private String cacheDataSourceType = null;
/**
* This is the location of the file to load (or reload)
*/
protected String cacheDataSourceName = null;
/**
* This is our connection object
*/
protected Connection JDBCcon;
/**
* this is the persistent result set that we use to incrementally get the
* records
*/
protected ResultSet mrs = null;
// these are the statements that we have to prepare to be able to get
// records once and only once
private static String cacheDataSelectQuery;
/**
* these are the prepared statements
*/
protected static PreparedStatement stmtCacheDataSelectQuery;
/**
* The internal cache is very simply a huge array. No black magic here
*/
protected Map<Long, BalanceGroup> balanceCache;
// used for handling date conversions
private static ConversionUtils conv;
// if we have to save a snapshot even when in DB mode
private boolean saveSnapshot = false;
// List of Services that this Client supports
private final static String SERVICE_DUMP_BALGROUP = "DumpBalGroup";
/** Constructor
* Creates a new instance of the Balance Cache.
*/
public BalanceCache()
{
// Initialise the cache hash
balanceCache = new ConcurrentHashMap<>(1000);
// Initialise variables that we will be using regularly - this is the
// default that can be overwritten using "setDateFormat"
conv = new ConversionUtils();
conv.setInputDateFormat("yyyyMMddHHmmss");
}
/**
* Add a value into the BalanceCache. Does not check for current existence.
* Primarily intended for loading into the cache, because the record id is
* managed by this method.
*
* @param BalanceGroupId The balance group identifier
* @param CounterId The ID of the counter in the balance group
* @param RecId The ID of the counter period of the counter
* @param ValidFrom The start of the validity period for the counter period
* @param ValidTo The end of the validity period for the counter period
* @param CurrentBal The current bal to assign to the counter period
*/
protected void addCounterAutoRecId(long BalanceGroupId, int CounterId, int RecId, long ValidFrom, long ValidTo, double CurrentBal)
{
BalanceGroup tmpBalGrp;
if (balanceCache.containsKey(BalanceGroupId))
{
// Add the balance to the existing group
tmpBalGrp = balanceCache.get(BalanceGroupId);
tmpBalGrp.addCounter(CounterId,RecId,ValidFrom,ValidTo,CurrentBal);
}
else
{
// Create a new group
tmpBalGrp = new BalanceGroup();
balanceCache.put(BalanceGroupId,tmpBalGrp);
tmpBalGrp.addCounter(CounterId,RecId,ValidFrom,ValidTo,CurrentBal);
}
}
/**
* Add a value into the BalanceCache. Does not check for current existence.
* Intended for use by user applications, as it lets the counter group manage
* the record id.
*
* @param BalanceGroupId The balance group identifier
* @param CounterId The ID of the counter in the balance group
* @param ValidFrom The start of the validity period for the counter period
* @param ValidTo The end of the validity period for the counter period
* @param CurrentBal The current bal to assign to the counter period
* @return The counter
*/
public Counter addCounter(long BalanceGroupId, int CounterId, long ValidFrom, long ValidTo, double CurrentBal)
{
BalanceGroup tmpBalGrp;
if (balanceCache.containsKey(BalanceGroupId))
{
// Add the balance to the existing group
tmpBalGrp = balanceCache.get(BalanceGroupId);
return tmpBalGrp.addCounter(CounterId,ValidFrom,ValidTo,CurrentBal);
}
else
{
// Create a new group
tmpBalGrp = new BalanceGroup();
balanceCache.put(BalanceGroupId,tmpBalGrp);
return tmpBalGrp.addCounter(CounterId,ValidFrom,ValidTo,CurrentBal);
}
}
/**
* Check if a counter exists in the balance cache, and return it if it does
*
* @param BalanceGroupId The balance group id
* @param CounterId The counter group id
* @param UTCEventDate The date of the counter
* @return false if it does not exist, otherwise true
*/
public Counter checkCounterExists(long BalanceGroupId, int CounterId, long UTCEventDate)
{
BalanceGroup tmpBalGrp = getBalanceGroup(BalanceGroupId);
CounterGroup tmpCounterGroup;
Counter tmpCounter;
// Check the balance group existence
if (tmpBalGrp == null)
{
return null;
}
else
{
// check the counter group existence
tmpCounterGroup = tmpBalGrp.getCounterGroup(CounterId);
if (tmpCounterGroup == null)
{
return null;
}
else
{
// Check the counter existence
tmpCounter = tmpCounterGroup.getCounterByUTCDate(UTCEventDate);
if (tmpCounter == null)
{
return null;
}
else
{
return tmpCounter;
}
}
}
}
/**
* Add a new balance group into the BalanceCache.
*
* @param BalanceGroupId The balance group identifier
* @return The balance group object
*/
public BalanceGroup addBalanceGroup(long BalanceGroupId)
{
BalanceGroup tmpBalGrp = null;
if (!balanceCache.containsKey(BalanceGroupId))
{
// Create a new group
tmpBalGrp = new BalanceGroup();
balanceCache.put(BalanceGroupId,tmpBalGrp);
}
return tmpBalGrp;
}
/**
* Gets a counter from a balance group by counter id and UTC date
*
* @param BalanceGroupId The balance group to retrieve for
* @param counterId The counter id to retrieve for
* @param UTCEventDate The date to retrieve for
* @return The counter or null
*/
public Counter getCounter(long BalanceGroupId, int counterId, long UTCEventDate)
{
BalanceGroup tmpBalGrp = getBalanceGroup(BalanceGroupId);
CounterGroup tmpCounterGroup;
Counter tmpCounter;
if (tmpBalGrp == null)
{
return null;
}
else
{
// Get the counter for euro
tmpCounterGroup = tmpBalGrp.getCounterGroup(counterId);
if (tmpCounterGroup == null)
{
return null;
}
else
{
// Find the right counter from the group - this will usually be the first
tmpCounter = tmpCounterGroup.getCounterByUTCDate(UTCEventDate);
if (tmpCounter == null)
{
return null;
}
else
{
return tmpCounter;
}
}
}
}
/**
* Gets a balance from the cache - performs no locking or any other concurrency
* control.
*
* @param balanceGroupId The ID of the balance group to retrieve
* @return The balance group
*/
public BalanceGroup getBalanceGroup(long balanceGroupId)
{
BalanceGroup tmpBalGrp;
if (!balanceCache.containsKey(balanceGroupId))
{
// create the default balance group
// throw a wobbly?
}
// Return the value
tmpBalGrp = balanceCache.get(balanceGroupId);
return tmpBalGrp;
}
/**
* loadCache is called automatically on startup of the
* cache factory, as a result of implementing the CacheLoader
* interface.
*
* @param ResourceName The name of the resource to load for
* @param CacheName The name of the cache to load for
* @throws OpenRate.exception.InitializationException
*/
@Override
public void loadCache(String ResourceName, String CacheName)
throws InitializationException
{
// If we found the type for the data source
boolean foundCacheDataSourceType = false;
// date format
String tmpDateFormat;
// Get the source of the data to load
setSymbolicName(CacheName);
// Find the location of the configuration data
OpenRate.getOpenRateFrameworkLog().info("Starting cache loading for <" + getSymbolicName() + ">");
cacheDataSourceType = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName,
CacheName,
"DataSourceType",
"None");
if (cacheDataSourceType.equalsIgnoreCase("File") | cacheDataSourceType.equalsIgnoreCase("DB"))
{
foundCacheDataSourceType = true;
}
if (!foundCacheDataSourceType)
{
message = "DataSourceType for cache <" +
getSymbolicName() +
"> must be File or DB, found <" +
cacheDataSourceType + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,getSymbolicName());
}
// get the date format
tmpDateFormat = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName,
CacheName,
"DateFormat",
"None");
if (tmpDateFormat.equalsIgnoreCase("None") == false)
{
conv.setInputDateFormat(tmpDateFormat);
}
// Get the configuration we are working on
if (cacheDataSourceType.equalsIgnoreCase("File"))
{
cacheDataSourceName = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName,
CacheName,
"DataFile",
"None");
if (cacheDataSourceName.equals("None"))
{
message = "Data source file name not found for cache <" + getSymbolicName() + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,getSymbolicName());
}
else
{
OpenRate.getOpenRateFrameworkLog().debug("Found Cache Data File <" + cacheDataSourceName + "> for cache <" + getSymbolicName() + ">");
}
loadDataFromFile();
}
if (cacheDataSourceType.equalsIgnoreCase("DB"))
{
// Get the data source name
cacheDataSourceName = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName,
CacheName,
"DataSource",
"None");
if (cacheDataSourceName.equals("None"))
{
message = "Data source DB name not found for cache <" + getSymbolicName() + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,getSymbolicName());
}
else
{
OpenRate.getOpenRateFrameworkLog().debug("Found Cache Data DB <" + cacheDataSourceName + "> for cache <" + getSymbolicName() + ">");
}
// Get the Select statement
cacheDataSelectQuery = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName,
CacheName,
"SelectStatement",
"None");
if (cacheDataSelectQuery.equals("None"))
{
message = "Data source select statement not found for cache <" +
getSymbolicName() + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,getSymbolicName());
}
else
{
OpenRate.getOpenRateFrameworkLog().debug(
"Found Select Query <" + cacheDataSelectQuery + "> for cache <" +
getSymbolicName() + ">");
}
// The datasource property was added to allow database to database
// JDBC adapters to work properly using 1 configuration file.
if(DBUtil.initDataSource(cacheDataSourceName) == null)
{
message = "Could not initialise DB connection <" + cacheDataSourceName + "> to in module <" + getSymbolicName() + ">.";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,getSymbolicName());
}
// See if we save snapshots on shutdown
saveSnapshot = Boolean.valueOf(PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName,
CacheName,
"SaveSnapshotToFile",
"false"));
loadDataFromDB();
}
}
/**
* Load the data from the defined file.
*
* @throws InitializationException
*/
public void loadDataFromFile() throws InitializationException
{
// Variable declarations
int balsLoaded = 0;
BufferedReader inFile;
String tmpFileRecord;
String[] balFields;
// Loading fields
String tmpBalGrpId;
String tmpCounterId;
String tmpRecId;
String tmpValidFrom;
String tmpValidTo;
String tmpCurrentBal;
double currentBal;
long validTo = 0;
long validFrom = 0;
int recId;
int counterId;
long balanceGroupId;
// Log that we are starting the loading
OpenRate.getOpenRateFrameworkLog().info("Starting Balance Cache Loading from File");
// Try to open the file
try
{
inFile = new BufferedReader(new FileReader(cacheDataSourceName));
}
catch (FileNotFoundException exFileNotFound)
{
message = "Application is not able to read file : <" +
cacheDataSourceName + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,
exFileNotFound,
getSymbolicName());
}
// File open, now get the stuff
try
{
while (inFile.ready())
{
tmpFileRecord = inFile.readLine();
if ((tmpFileRecord.startsWith("#")) |
tmpFileRecord.trim().equals(""))
{
// Comment line, ignore
}
else
{
balFields = tmpFileRecord.split(";");
tmpBalGrpId = balFields[0];
tmpCounterId = balFields[1];
tmpRecId = balFields[2];
tmpValidFrom = balFields[3];
tmpValidTo = balFields[4];
tmpCurrentBal = balFields[5];
balanceGroupId = Long.parseLong(tmpBalGrpId);
counterId = Integer.parseInt(tmpCounterId);
recId = Integer.parseInt(tmpRecId);
try
{
validFrom = conv.convertInputDateToUTC(tmpValidFrom);
}
catch (ParseException ex)
{
message = "Error converting date in cache <" + getSymbolicName() + ">. Could not convert date <" + tmpValidFrom + "> using formatter <" + conv.getInputDateFormat() + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,getSymbolicName());
}
try
{
validTo = conv.convertInputDateToUTC(tmpValidTo);
}
catch (ParseException ex)
{
message = "Error converting date in cache <" + getSymbolicName() + ">. Could not convert date <" + tmpValidFrom + "> using formatter <" + conv.getInputDateFormat() + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,getSymbolicName());
}
currentBal = Double.parseDouble(tmpCurrentBal);
addCounterAutoRecId(balanceGroupId,counterId,recId,validFrom,validTo,currentBal);
balsLoaded++;
}
}
}
catch (IOException ex)
{
OpenRate.getOpenRateFrameworkLog().fatal(
"Error reading input file <" + cacheDataSourceName +
"> in record <" + balsLoaded + ">. IO Error.");
}
catch (ArrayIndexOutOfBoundsException ex)
{
OpenRate.getOpenRateFrameworkLog().fatal(
"Error reading input file <" + cacheDataSourceName +
"> in record <" + balsLoaded + ">. Malformed Record.");
}
finally
{
try
{
inFile.close();
}
catch (IOException ex)
{
OpenRate.getOpenRateFrameworkLog().error("Error closing input file <" + cacheDataSourceName +
">", ex);
}
}
OpenRate.getOpenRateFrameworkLog().info(
"Balance Cache Data Loading completed. " + balsLoaded +
" configuration lines loaded from <" + cacheDataSourceName +
">");
}
/**
* Load the data from the defined Data Source.
*
* @throws InitializationException
*/
public void loadDataFromDB() throws InitializationException
{
long balsLoaded = 0;
// Loading fields
String tmpBalGrpId;
String tmpCounterId;
String tmpRecId;
String tmpValidFrom;
String tmpValidTo;
String tmpCurrentBal;
double CurrentBal;
long validTo;
long validFrom;
int recId;
int counterId;
long balanceGroupId;
// Log that we are starting the loading
OpenRate.getOpenRateFrameworkLog().info("Starting Balance 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 Sex)
{
message = "Error performing SQL for retieving Balance data. message <" + Sex.getMessage() + ">";
OpenRate.getOpenRateFrameworkLog().fatal(message);
throw new InitializationException(message,getSymbolicName());
}
// loop through the results for the balance cache
try
{
while (mrs.next())
{
tmpBalGrpId = mrs.getString(1);
tmpCounterId = mrs.getString(2);
tmpRecId = mrs.getString(3);
tmpValidFrom = mrs.getString(4);
tmpValidTo = mrs.getString(5);
tmpCurrentBal = mrs.getString(6);
balanceGroupId = Long.parseLong(tmpBalGrpId);
counterId = Integer.parseInt(tmpCounterId);
recId = Integer.parseInt(tmpRecId);
try
{
validFrom = conv.convertInputDateToUTC(tmpValidFrom);
}
catch (ParseException ex)
{
message = "Error converting date in cache <" + getSymbolicName() + ">. Could not convert date <" + tmpValidFrom + "> using formatter <" + conv.getInputDateFormat() + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,getSymbolicName());
}
try
{
validTo = conv.convertInputDateToUTC(tmpValidTo);
}
catch (ParseException ex)
{
message = "Error converting date in cache <" + getSymbolicName() + ">. Could not convert date <" + tmpValidFrom + "> using formatter <" + conv.getInputDateFormat() + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,getSymbolicName());
}
CurrentBal = Double.parseDouble(tmpCurrentBal);
addCounterAutoRecId(balanceGroupId,counterId,recId,validFrom,validTo,CurrentBal);
balsLoaded++;
}
}
catch (SQLException ex)
{
message = "Error opening retreiving customer data. SQL error: " + ex.getMessage();
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 Result Set for Customer information from <" +
cacheDataSourceName + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,ex,getSymbolicName());
}
OpenRate.getOpenRateFrameworkLog().info("Balance Loading completed. " + balsLoaded +
" lines loaded from <" + cacheDataSourceName + ">");
}
// -----------------------------------------------------------------------------
// ------------- 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
{
//Register this Client
ClientManager.getClientManager().registerClient("Resource",getSymbolicName(), this);
//Register services for this Client
ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_DUMP_BALGROUP, 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)
{
int ResultCode = -1;
String filename;
if (Command.equalsIgnoreCase(SERVICE_DUMP_BALGROUP))
{
switch (Parameter) {
case "":
// do nothing
break;
case "All":
// calculate the file name
filename = "Dump-" + getSymbolicName() + "-All-" + Calendar.getInstance().getTimeInMillis() + ".dump";
dumpBalanceGroups(filename);
return "Dumped all balance groups to file <" + filename + ">";
default:
long tmpBalGroup;
// See if the Parameter is numeric
try
{
tmpBalGroup = Long.valueOf(Parameter);
}
catch (NumberFormatException nfe)
{
return "Balance group parameter was not numeric <" + Parameter + ">";
}
// try to get the balance group
if (balanceCache.containsKey(tmpBalGroup) == false)
{
return "Could not locate balance group <" + tmpBalGroup + ">";
}
else
{
// calculate the file name
filename = "Dump-" + getSymbolicName() + "-BalGroup-" + tmpBalGroup + "-" + Calendar.getInstance().getTimeInMillis() + ".dump";
dumpBalanceGroup(filename,tmpBalGroup);
return "Dumped balance group <" + tmpBalGroup + "> to file <" + filename + ">";
}
}
}
if (ResultCode == 0)
{
OpenRate.getOpenRateFrameworkLog().debug(LogUtil.LogECICacheCommand(getSymbolicName(), Command, Parameter));
return "OK";
}
else
{
return "Command Not Understood";
}
}
// -----------------------------------------------------------------------------
// ---------------- Start of data base data layer functions --------------------
// -----------------------------------------------------------------------------
/**
* PrepareStatements creates the statements from the SQL expressions
* so that they can be run as needed.
*
* @throws InitializationException
*/
protected void prepareStatements()
throws InitializationException
{
try
{
// prepare the SQL for the TestStatement
stmtCacheDataSelectQuery = JDBCcon.prepareStatement(cacheDataSelectQuery,
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
}
catch (SQLException ex)
{
message = "Error preparing the statement " + cacheDataSelectQuery;
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,getSymbolicName());
}
}
/**
* This saves the cache back to the location on framework shutdown. Note that
* this information is not used on startup again, but serves only as a snapshot
* of the status that there was at the given time.
*
* For the balance cache, we save a snapshot of the balances,
*/
@Override
public void saveCache()
{
BufferedWriter outFile;
Long tmpBalGrpKey;
Integer tmpCounterId;
BalanceGroup tmpBalGrp;
Iterator<Long> balGrpIter;
Iterator<Integer> counterIter;
String stringToWrite;
CounterGroup tmpCounterGroup;
ArrayList<Counter> counters;
int i;
Counter tmpCounter;
String fileName = null;
if (cacheDataSourceType.equalsIgnoreCase("File")) {
// Use the defined name
fileName = cacheDataSourceName;
} else if (cacheDataSourceType.equalsIgnoreCase("DB") && saveSnapshot) {
// Set the timestamp
long timestamp = ConversionUtils.getConversionUtilsObject().getCurrentUTCms();
fileName = "BalCache_" + getSymbolicName() + "_" + timestamp + ".dump";
}
if (fileName != null) {
// Log that we are starting the saving
OpenRate.getOpenRateFrameworkLog().info("Starting Balance Cache Saving to File: " + fileName);
// Try to open the file
try
{
outFile = new BufferedWriter(new FileWriter(fileName));
outFile.write("# Balance data storage file");
outFile.newLine();
outFile.newLine();
// get a list of all the balance groups
balGrpIter = balanceCache.keySet().iterator();
while (balGrpIter.hasNext())
{
tmpBalGrpKey = balGrpIter.next();
tmpBalGrp = balanceCache.get(tmpBalGrpKey);
// Get a list of all the counter groups in the balance group
counterIter = tmpBalGrp.getCounterIterator();
while (counterIter.hasNext())
{
// get a list of all the counters in the group
tmpCounterId = counterIter.next();
tmpCounterGroup = tmpBalGrp.getCounterGroup(tmpCounterId);
counters = tmpCounterGroup.getCounters();
for (i = 0 ; i < counters.size() ; i++)
{
tmpCounter = counters.get(i);
stringToWrite = tmpBalGrpKey + ";" + tmpCounterId + ";" +
tmpCounter.RecId + ";" + tmpCounter.validFrom + ";" +
tmpCounter.validTo + ";" + tmpCounter.CurrentBalance;
outFile.write(stringToWrite);
outFile.newLine();
}
}
}
outFile.flush();
outFile.close();
}
catch (IOException IOex)
{
OpenRate.getOpenRateFrameworkLog().error(
"Application is not able to write file : <" +
cacheDataSourceName + ">");
}
// Log that we have finished the saving
OpenRate.getOpenRateFrameworkLog().info("Finished Balance Cache Saving to File");
}
}
// -----------------------------------------------------------------------------
// -------------------- Start of local utility functions -----------------------
// -----------------------------------------------------------------------------
/**
* Dumps a balance group to file.
*
* @param filename The file to dump to
* @param tmpBalGroup The balance group we are dumping
*/
private void dumpBalanceGroup(String filename, long tmpBalGroup)
{
OpenRate.getOpenRateFrameworkLog().info("Dumping balance group <" + tmpBalGroup + "> to file <" + filename + ">");
try
{
try (BufferedWriter outFile = new BufferedWriter(new FileWriter(filename)))
{
outFile.write("# Balance data dump file\n");
BalanceGroup balanceGroup = balanceCache.get(tmpBalGroup);
Iterator<Integer> counterGrpIter = balanceGroup.getCounterIterator();
while (counterGrpIter.hasNext())
{
int counterId = counterGrpIter.next();
CounterGroup counter = balanceGroup.getCounterGroup(counterId);
ArrayList<Counter> counterList = counter.getCounters();
Iterator<Counter> counterIter = counterList.iterator();
while (counterIter.hasNext())
{
Counter tmpCounter = counterIter.next();
outFile.write("BalanceGroup: " + tmpBalGroup +
", CounterID: " + counterId +
", Rec id: " + tmpCounter.RecId +
", valid: " + tmpCounter.validFrom + "-" + tmpCounter.validTo +
", currentBal: " + tmpCounter.CurrentBalance +
"\n");
}
}
outFile.flush();
outFile.close();
}
}
catch (IOException ioex)
{
OpenRate.getOpenRateFrameworkLog().error("Exception <" + ioex.getMessage() + "> while dumping balance group <" + tmpBalGroup + "> to file <" + filename + ">");
}
OpenRate.getOpenRateFrameworkLog().info("Dumping balance group <" + tmpBalGroup + "> to file <" + filename + "> completed");
}
/**
* Dumps all available balance groups to file.
*
* @param filename The file to dump to
*/
private void dumpBalanceGroups(String filename)
{
OpenRate.getOpenRateFrameworkLog().info("Dumping balance groups to file <" + filename + ">");
try
{
try (BufferedWriter outFile = new BufferedWriter(new FileWriter(filename))) {
outFile.write("# Balance data dump file\n");
Iterator<Long> balIter = balanceCache.keySet().iterator();
while (balIter.hasNext())
{
long balanceGroupId = balIter.next();
BalanceGroup balanceGroup = balanceCache.get(balanceGroupId);
Iterator<Integer> counterGrpIter = balanceGroup.getCounterIterator();
while (counterGrpIter.hasNext())
{
int counterId = counterGrpIter.next();
CounterGroup counter = balanceGroup.getCounterGroup(counterId);
ArrayList<Counter> counterList = counter.getCounters();
Iterator<Counter> counterIter = counterList.iterator();
while (counterIter.hasNext())
{
Counter tmpCounter = counterIter.next();
outFile.write("BalanceGroup: " + balanceGroupId +
", CounterID: " + counterId +
", Rec id: " + tmpCounter.RecId +
", valid: " + tmpCounter.validFrom + "-" + tmpCounter.validTo +
", currentBal: " + tmpCounter.CurrentBalance +
"\n");
}
}
}
}
}
catch (IOException ioex)
{
OpenRate.getOpenRateFrameworkLog().error("Exception <" + ioex.getMessage() + "> while dumping balance groups to file <" + filename + ">");
}
OpenRate.getOpenRateFrameworkLog().info("Dumping balance groups to file <" + filename + "> completed");
}
}