/* ====================================================================
* 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.CommonConfig;
import OpenRate.OpenRate;
import OpenRate.configurationmanager.ClientManager;
import OpenRate.db.DBUtil;
import OpenRate.exception.InitializationException;
import OpenRate.exception.ProcessingException;
import OpenRate.lang.AuditSegment;
import OpenRate.lang.CustInfo;
import OpenRate.lang.CustProductInfo;
import OpenRate.lang.ProductList;
import OpenRate.logging.ILogger;
import OpenRate.logging.LogUtil;
import OpenRate.utils.PropertyUtils;
import java.io.*;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
/**
* This class implements a cache of customer information for use in
* rating based on product instances, reading the data from a Portal database.
* This uses the audit tables, so that changes to the product list are fully
* historicised.
*
* The loading process goes like this:
* 1) Get a list of the aliases and the associated service IDs. This means that
* we are able to deal with aliases more easily later
* 2) Get a list of all the audit segments (history segments) for each service
* 3) For each segment, get a list of the products, with validity
*
* ------------------------------ File Interface -------------------------------
*
* The verbs in the file are:
*
* 01 = Add Customer Account Audit Segments
* 02 = Add Customer Product
* 05 = Add Alias
* 06 = Add ERA
*
* Verb descriptions:
*
* 01, Add Customer Account Audit Segment
* Record format:
* 01;AuditSegID;CustID;ExternalCustId;AudSegDate;ValidFrom;ValidTo
*
* 02, Add Customer Product
* Record format:
* 02;AuditSegID;ProductIdentifier;SubscriptionIdentifier;Service;ValidFrom;ValidTo
*
* 05, Add Alias
* Record format:
* 05;Alias;CustomerIdentifier,SubscriptionIdentifier,AliasValidFrom,AliasValidTo
*
* 06, Add ERA
* Record format:
* 06;AuditSegID;ERAKey;ERAValue
*
* The verbs presented here do not allow dynamic update, and in order to do this
* additional verbs will have to be created.
*
* ------------------------------- DB Interface --------------------------------
*
* If the loading is done from a DB, the queries that are executed are:
*
* AliasSelectQuery: This query should return a list of the aliases that are
* to be associated with each account, and the subscription that they are
* associated with on the account. The query should return:
*
* 1) ALIAS_IDENTIFIER (used for incremental update)
* 2) ALIAS
* 3) CUSTOMER_IDENTIFIER
* 4) SUBSCRIPTION_IDENTIFIER
* 5) VALID_FROM (YYYYMMDDHHMMSS)
* 6) VALID_TO (YYYYMMDDHHMMSS)
* 7) ModT (YYYYMMDDHHMMSS)
*
* AuditSegmentSelectStatement: This query returns a list of all of the customer
* accounts that the system should handle. The query should return:
*
* 1) AUDIT_SEGMENT_IDENTIFIER (used for incremental update)
* 2) CUSTOMER_IDENTIFIER
* 3) ExternalCustId
* 4) AUDIT_SEGMENT_VALID_FROM (YYYYMMDDHHMMSS)
* 5) VALID_FROM (YYYYMMDDHHMMSS)
* 6) VALID_TO (YYYYMMDDHHMMSS)
* 7) ModT (YYYYMMDDHHMMSS)
*
* ProductSelectQuery: This query returns a list of all the products that are
* associated with a customer account. The query should return:
*
* 1) PRODUCT_ID (used for incremental update)
* 2) AUDIT_SEGMENT_IDENTIFIER
* 3) PRODUCT_NAME
* 4) SUBSCRIPTION_IDENTIFIER
* 5) SERVICE
* 6) VALID_FROM (YYYYMMDDHHMMSS)
* 7) VALID_TO (YYYYMMDDHHMMSS)
* 8) ModT (YYYYMMDDHHMMSS)
*
* ERASelectQuery: This query returns a list of all the ERA that are associated
* with a customer account. The query should return:
*
* 1) ERA_ID
* 2) AUDIT_SEGMENT_IDENTIFIER
* 3) ERA_KEY
* 4) ERA_VALUE
* 5) ModT (YYYYMMDDHHMMSS)
*
* ModT is used to track changes and merge them into the existing cache. Each
* time a change is made, ModT in the DB is updated, and we use this fact
* to gather the changes and either add new data or update existing data.
*
* Generally we know to update by the fact that we have a new ModT for an
* existing AuditSegID.
*
* @author i.sparkes
*/
public class CustomerCacheAudited
extends AbstractSyncLoaderCache
{
// this is the persistent result set that we use to incrementally get the records
protected ResultSet crs = null;
protected ResultSet ars = null;
protected ResultSet prs = null;
protected ResultSet ers = null;
// these are the statements that we have to prepare to be able to get
// records once and only once
protected String aliasSelectQuery;
protected String auditSelectQuery;
protected String productSelectQuery;
protected String eraSelectQuery;
// these are the prepared statements
protected PreparedStatement stmtAliasSelectQuery;
protected PreparedStatement stmtAuditSelectQuery;
protected PreparedStatement stmtProductSelectQuery;
protected PreparedStatement stmtERASelectQuery;
/**
* Used to allow alias maps - go in with the alias, and it will return the
* unique customer AuditSegID for you
*/
protected ConcurrentHashMap<String,ValidityNode> aliasCache;
/**
* This stores the history segments of the product information. Go in with the
* customer AuditSegID and it returns all the versions of the information for that cust.
*/
protected ConcurrentHashMap<Integer, CustInfo> custCache;
/**
* This stores the customer history segment to the products. Go in with the history
* segment and it returns the product list for that segment.
*/
protected ConcurrentHashMap<Long, AuditSegment> auditSegmentCache;
/**
* The internal date format is the format that by default will be used when
* interpreting dates that come in the queries. The value here is the default
* value, but this can be changed.
*/
protected String internalDateFormat = "yyyyMMddHHmmss";
// List of Services that this Client supports
protected final static String SERVICE_UPDATE_FREQUENCY = "UpdateFrequency";
protected final static String SERVICE_DUMP_INFO = "DumpInfo";
// this is the update frequency that will determine how often the
// cache information is updated from the DB
protected int updateFrequency = 300;
// this is the last time that we performed an update
protected long lastUpdate;
// These are the ModT values that we use to perform incremental updates
protected long lastAccountVerModT = 0;
protected long lastAliasModT = 0;
protected long lastERAModT = 0;
protected long lastProductModT = 0;
/**
* A validityNode is a segment of validity of a resource. These are chained
* together in a sorted linked list. The sorting is done at insertion time
* into the list, meaning that lookups at run time can be optimised.
*/
protected class ValidityNode
{
long ID; // Required for managing updates
long validFrom;
long validTo;
int custId = 0;
String subId = "";
ValidityNode child = null;
}
/**
* Constructor
* Creates a new instance of the Customer Cache. The Cache
* contains all of the Customer IDs that have been cached.
*/
public CustomerCacheAudited()
{
super();
aliasCache = new ConcurrentHashMap<>(5000);
custCache = new ConcurrentHashMap<>(5000);
auditSegmentCache = new ConcurrentHashMap<>(5000);
}
// -----------------------------------------------------------------------------
// ------------------ Start of loading internal functions ----------------------
// -----------------------------------------------------------------------------
/**
* Add a alias into the cache. If the alias already exists, then we
* update the cache information.
*
* @param ID The ID of the alias, used for managing updates to an existing row
* @param alias The alias alias we want to store
* @param custId The customer AuditSegID that the alias refers to
* @param subID The subscription AuditSegID for this alias
* @param validFrom The start of the validity for this alias
* @param validTo The end of the validity for this alias
*/
public void addAlias(long ID, String alias, Integer custId, String subID, long validFrom, long validTo)
{
ValidityNode newNode;
ValidityNode tmpValidityNode;
ValidityNode tmpValidityNextNode;
long lastValidTo;
long tmpTimeFrom;
// Check that the valid to is after the valid from
if (validFrom > validTo)
{
// Otherwise write an error and ignore it
OpenRate.getOpenRateFrameworkLog().error("Alias ID <" + alias + "> validity period from <" + validFrom + "> is after validity period to <" + validTo + ">. Ignoring.");
return;
}
// Now add the validity segment into the list
if (!aliasCache.containsKey(alias))
{
// We do not know this alias - Create the new list
tmpValidityNode = new ValidityNode();
tmpValidityNode.ID = ID;
tmpValidityNode.validFrom = validFrom;
tmpValidityNode.validTo = validTo;
tmpValidityNode.custId = custId;
tmpValidityNode.subId = subID;
tmpValidityNode.child = null;
// Add in the new node
aliasCache.put(alias, tmpValidityNode);
// Done
return;
}
else
{
// Recover the validity map that there is
tmpValidityNode = aliasCache.get(alias);
// preset our valid to date
lastValidTo = CommonConfig.LOW_DATE;
// now run down the validity periods until we find the right position
while (tmpValidityNode != null)
{
tmpValidityNextNode = tmpValidityNode.child;
if (tmpValidityNextNode != null)
{
tmpTimeFrom = tmpValidityNextNode.validFrom;
}
else
{
tmpTimeFrom = CommonConfig.HIGH_DATE;
}
// Check to see if we have a simple update
if ((tmpValidityNode.ID == ID) && (ID != 0))
{
// This is a simple update
tmpValidityNode.validFrom = validFrom;
tmpValidityNode.validTo = validTo;
tmpValidityNode.custId = custId;
tmpValidityNode.subId = subID;
// done
return;
}
if ((validFrom > tmpValidityNode.validTo) &
(tmpValidityNextNode == null))
{
// insert at the tail of the list if we are able
newNode = new ValidityNode();
tmpValidityNode.child = newNode;
newNode.ID = ID;
newNode.validFrom = validFrom;
newNode.validTo = validTo;
newNode.custId = custId;
newNode.subId = subID;
// done
return;
}
else if ((validFrom > lastValidTo) &
(validTo <= tmpValidityNode.validFrom))
{
// insert at the head of the list
newNode = new ValidityNode();
// move the information over
newNode.ID = tmpValidityNode.ID;
newNode.validFrom = tmpValidityNode.validFrom;
newNode.validTo = tmpValidityNode.validTo;
newNode.subId = tmpValidityNode.subId;
newNode.custId = tmpValidityNode.custId;
newNode.child = tmpValidityNode.child;
// add the new information
tmpValidityNode.ID = ID;
tmpValidityNode.validFrom = validFrom;
tmpValidityNode.validTo = validTo;
tmpValidityNode.subId = subID;
tmpValidityNode.custId = custId;
tmpValidityNode.child = newNode;
// done
return;
}
else if ((validFrom > tmpValidityNode.validTo) & (validTo <= tmpTimeFrom))
{
// insert in the middle of the list
newNode = new ValidityNode();
newNode.child = tmpValidityNode.child;
tmpValidityNode.child = newNode;
newNode.ID = ID;
newNode.validFrom = validFrom;
newNode.validTo = validTo;
newNode.custId = custId;
newNode.subId = subID;
// done
return;
}
// Move down the map
lastValidTo = tmpValidityNode.validTo;
tmpValidityNode = tmpValidityNode.child;
}
}
// if we get here, we could not insert correctly
message = "Alias ID <" + alias + "> already exists for time <" + validFrom + "-" + validTo + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
}
/**
* Add an audit segment into the CustomerCache.
* The identifier here of the customer is the internal key
*
* @param custId The internal customer account identifier
* @param balanceGroup The AuditSegID of the balance group to use for this account
* @param ExtCustID The external customer ID
* @param auditSegId The AuditSegID of the audit segment
* @param audSegValidFrom The start of the validity of this audit segment
* @param custValidFrom The start of the validity of the customer for this audit segment
* @param custValidTo The end of the validity of the customer for this audit segment
* @throws InitializationException
*/
public void addAuditSegment(long auditSegId,
Integer custId,
String ExtCustID,
long balanceGroup,
long audSegValidFrom,
long custValidFrom,
long custValidTo) throws InitializationException
{
CustInfo tmpCustInfo;
AuditSegment tmpAuditSegment;
// See if we already have AuditSegID for this customer
if (!custCache.containsKey(custId))
{
// If not, create the new entry for the customer AuditSegID
tmpCustInfo = new CustInfo();
tmpCustInfo.ExternalCustId = ExtCustID;
tmpCustInfo.balanceGroup = balanceGroup;
custCache.put(custId,tmpCustInfo);
}
else
{
// Otherwise just recover it
tmpCustInfo = custCache.get(custId);
}
// See if we are dealing with an update - we do this by searching for the AuditSegID
tmpAuditSegment = tmpCustInfo.getAuditSegmentByID(auditSegId);
if (tmpAuditSegment != null)
{
// We are doing an update - update the validity dates
tmpAuditSegment.setUTCSegmentValidFrom(audSegValidFrom);
tmpAuditSegment.setUTCAccountValidFrom(custValidFrom);
tmpAuditSegment.setUTCAccountValidTo(custValidTo);
// done - get out
return;
}
// Creating a new audit segment - will return null if it fails
tmpAuditSegment = tmpCustInfo.createAuditSegment(audSegValidFrom);
if (tmpAuditSegment == null)
{
// Null response means that we were not able to cover the period
// without overlaps
message = "Attempting to add an audit segment to custId <" + custId + "> with start date <" + audSegValidFrom + ">, but this already exists";
OpenRate.getOpenRateFrameworkLog().error(message);
}
else
{
// Set the audit segment information
tmpAuditSegment.setAuditSegmentID(auditSegId);
tmpAuditSegment.setUTCAccountValidFrom(custValidFrom);
tmpAuditSegment.setUTCAccountValidTo(custValidTo);
// put the information into the contruction cache
auditSegmentCache.put(auditSegId,tmpAuditSegment);
}
}
/**
* Add a CPI value into the CustomerCache
*
* The identifier here of the customer is the audit segment AuditSegID
*
* @param auditSegId The audit segment AuditSegID
* @param productRefId The reference value for this row, used to manage updates
* @param prodID The product name
* @param subId The subscription AuditSegID
* @param service The service
* @param prodValidFrom The start of the validity date of the product
* @param prodValidTo The end of the validity date of the product
* @throws InitializationException
*/
public void addAuditedCPI(long auditSegId, long productRefId, String prodID, String subId, String service,long prodValidFrom, long prodValidTo) throws InitializationException
{
AuditSegment tmpAuditSegment;
// Recover the audit segment
tmpAuditSegment = auditSegmentCache.get(auditSegId);
if (tmpAuditSegment == null)
{
message = "Attempting to add a product <" + productRefId + "> to a non-existent audit segment <" + auditSegId + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
}
else
{
// adjust the to high date
if (prodValidTo == 0)
{
prodValidTo = CommonConfig.HIGH_DATE;
}
// Add the product to the audit segment
tmpAuditSegment.getProductList().addProduct(productRefId,prodID,subId,service,prodValidFrom,prodValidTo,1);
}
}
/**
* Add an ERA value into the CustomerCache
*
* @param AuditSegId The audit segment AuditSegID to add the ERA to
* @param ERAKey The ERA key to add
* @param ERAValue The ERA Value to add
* @throws InitializationException
*/
public void addAuditedERA(long AuditSegId, String ERAKey, String ERAValue)
throws InitializationException
{
AuditSegment tmpAuditSegment;
// Recover the audit segment
tmpAuditSegment = auditSegmentCache.get(AuditSegId);
if (tmpAuditSegment == null)
{
message = "Attempting to add an ERA <" + ERAKey + "> to a non-existent audit segment <" + AuditSegId + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
}
else
{
// The put method manages the update or insert automatically
tmpAuditSegment.putERA(ERAKey,ERAValue);
}
}
// -----------------------------------------------------------------------------
// ---------------------- Start of retrieval functions -------------------------
// -----------------------------------------------------------------------------
/**
* Get a list of all products that are available at the time of the CDR, for a
* given Alias. Note that this takes into account the subId of the alias to
* limit the number of products returned. It does not however limit on the
* basis of the service.
*
* @param alias The alias to recover the product list for
* @param cdrDate The date to recover the product list for
* @return The products for the account and date
* @throws ProcessingException
*/
public ProductList getProducts(String alias, long cdrDate) throws ProcessingException
{
Integer custId = null;
ValidityNode tmpValidityNode;
String subId = null;
// See if we already have AuditSegID for this customer
if (aliasCache.containsKey(alias))
{
// get the start of the search tree
tmpValidityNode = aliasCache.get(alias);
// Now that we have the Validity Map, get the entry
while (tmpValidityNode != null)
{
if ((tmpValidityNode.validFrom <= cdrDate) &
(tmpValidityNode.validTo > cdrDate))
{
custId = tmpValidityNode.custId;
subId = tmpValidityNode.subId;
break;
}
// Move down the map
tmpValidityNode = tmpValidityNode.child;
}
if (custId == null)
{
// Otherwise write an error and ignore it
message = "Alias <" + alias + "> not found for time <" + cdrDate + ">. Lookup failed.";
throw new ProcessingException(message,getSymbolicName());
}
else
{
// recover the products with the Cust ID
return getProducts(custId,subId,cdrDate);
}
}
else
{
// Otherwise write an error and ignore it
message = "Alias <" + alias + "> not found. Lookup failed.";
throw new ProcessingException(message,getSymbolicName());
}
}
/**
* Get a list of all products that are available at the time of the CDR, for a
* given Alias. Note that this takes into account the subId of the alias to
* limit the number of products returned. It does not however limit on the
* basis of the service.
*
* @param CustId The customer ID o recover the products for
* @param SubscriptionID The subscription ID to recover for (null = all)
* @param CDRDate The date to recover the product list for
* @return The products for the account and date
* @throws ProcessingException
*/
public ProductList getProducts(Integer CustId, String SubscriptionID, long CDRDate) throws ProcessingException
{
CustInfo tmpCustInfo;
ProductList resultProductList;
AuditSegment tmpAuditSegment;
// Prepare the result
resultProductList = new ProductList();
// get the customer for the alias
tmpCustInfo = custCache.get(CustId);
//get the correct audit segment
tmpAuditSegment = tmpCustInfo.getBestAuditSegmentMatch(CDRDate);
if (tmpAuditSegment != null)
{
OpenRate.getOpenRateFrameworkLog().debug("Using audit segment <" + tmpAuditSegment.getAuditSegmentID() + "> for Cust ID <" + CustId + "> at time <" + CDRDate + ">");
// Check the validity of the customer account
if ((tmpAuditSegment.getUTCAccountValidFrom() <= CDRDate) && (tmpAuditSegment.getUTCAccountValidTo() > CDRDate))
{
// the account is valid - return the list
return getProducts(tmpAuditSegment, SubscriptionID);
}
else
{
// The account is not valid at this time
message = "Account id <" + CustId + "> not valid at time <" + CDRDate + "> in audit segment <" + tmpAuditSegment.getAuditSegmentID() + ">";
throw new ProcessingException(message,getSymbolicName());
}
}
return resultProductList;
}
/**
* Get a list of all the products from an audit segment, matching the
* subId.
*
* @param tmpAuditSegment The audit setment to recover the products from
* @param subId The subscription AuditSegID to recover the products for
* @return The products from the audit segment for the given sub AuditSegID
*/
public ProductList getProducts(AuditSegment tmpAuditSegment, String subId)
{
ProductList tmpProductList;
ProductList resultProductList;
int i;
// Prepare the result
resultProductList = new ProductList();
// Get the product list
tmpProductList = tmpAuditSegment.getProductList();
// Now remove the products that do not match the audit segment
if (subId == null)
{
// return all products
resultProductList = tmpProductList;
}
else
{
// get only the products that match the subscription
for (i = 0 ; i < tmpProductList.getProductCount() ; i++ )
{
if (tmpProductList.getProduct(i).getSubID().equals(subId))
{
resultProductList.addProduct(tmpProductList.getProduct(i));
}
}
}
return resultProductList;
}
/**
* Gets an internal custID for a given alias and date
*
* @param alias The alias for the customer account
* @param cdrDate The date to recover the internal cust AuditSegID for
* @return The internal customer AuditSegID
*/
public Integer getCustId(String alias, long cdrDate)
{
// See if we already have AuditSegID for this customer
if (alias!= null)
{
// get the start of the search tree
ValidityNode tmpValidityNode = aliasCache.get(alias);
// Now that we have the Validity Map, get the entry
while (tmpValidityNode != null)
{
if (isFound(tmpValidityNode, cdrDate))
{
return tmpValidityNode.custId;
}
// Move down the map
tmpValidityNode = tmpValidityNode.child;
}
}
// return the id
return null;
}
private boolean isFound(ValidityNode tmpValidityNode, long cdrDate)
{
return tmpValidityNode.validFrom <= cdrDate && tmpValidityNode.validTo > cdrDate ? true : false;
}
/**
* Gets an internal custID for a given alias and date
*
* @param alias The alias for the customer account
* @param cdrDate The date to recover the internal cust AuditSegID for
* @return The internal customer AuditSegID
*/
public String getSubscriptionId(String alias, long cdrDate)
{
// See if we already have AuditSegID for this customer
if (alias!= null)
{
// get the start of the search tree
ValidityNode tmpValidityNode = aliasCache.get(alias);
// Now that we have the Validity Map, get the entry
while (tmpValidityNode != null)
{
if (isFound(tmpValidityNode, cdrDate))
{
return tmpValidityNode.subId;
}
// Move down the map
tmpValidityNode = tmpValidityNode.child;
}
}
// return the id
return null;
}
/**
* Gets the internal custIDs for a given alias (there can be more than one)
*
* @param alias The alias for the customer account
* @return The internal customer AuditSegID
*/
public ArrayList<Integer> getAllCustId(String alias)
{
Integer custId;
ValidityNode tmpValidityNode;
ArrayList<Integer> tmpResult = new ArrayList<>();
// See if we already have AuditSegID for this customer
if (aliasCache.containsKey(alias))
{
// get the start of the search tree
tmpValidityNode = aliasCache.get(alias);
// Now that we have the Validity Map, get the entry
while (tmpValidityNode != null)
{
custId = tmpValidityNode.custId;
tmpResult.add(custId);
// Move down the map
tmpValidityNode = tmpValidityNode.child;
}
}
return tmpResult;
}
/**
* Gets a the Subscription AuditSegID for a given alias and date
*
* @param alias The alias to get the subscription AuditSegID for
* @param CDRDate The date to get the subscription AuditSegID for
* @return The subscription AuditSegID for the alias and date
*/
public String getSubId(String alias, long cdrDate)
{
// See if we already have AuditSegID for this customer
if (alias != null)
{
// get the start of the search tree
ValidityNode tmpValidityNode = aliasCache.get(alias);
// Now that we have the Validity Map, get the entry
while (tmpValidityNode != null)
{
if (isFound(tmpValidityNode, cdrDate))
{
return tmpValidityNode.subId;
}
// Move down the map
tmpValidityNode = tmpValidityNode.child;
}
}
return null;
}
/**
* Gets the audit segment for the CustId and the CDR date.
*
* @param custId The internal customer account AuditSegID to retrieve the audit seg for
* @param CDRDate The date to retrieve for
* @return The audit segment for the account and date
*/
public AuditSegment getAuditSegment(int custId, long CDRDate)
{
// get the customer for the alias
CustInfo tmpCustInfo = custCache.get(custId);
//get the correct audit segment
return tmpCustInfo.getBestAuditSegmentMatch(CDRDate);
}
/**
* Return the value of the balance group so that we are able to update
* it during the logic processing. This is to make sure that we have all
* the information necessary when we start the calculation.
*
* @param custID The internal customer AuditSegID to retrieve the balance group for
* @return The balance group AuditSegID
*/
public long getBalanceGroup(Integer custID)
{
CustInfo tmpCustInfo;
// Get the product information
tmpCustInfo = custCache.get(custID);
return tmpCustInfo.balanceGroup;
}
/**
* Return the value of the ExternalCustId associated with the Cust Id.
*
* @param custID The internal customer AuditSegID to get the ExternalCustId for
* @return The ExternalCustId
*/
public String getExtCustID(Integer custID)
{
CustInfo tmpCustInfo;
// Get the product information
tmpCustInfo = custCache.get(custID);
return tmpCustInfo.ExternalCustId;
}
/**
* Return the value of the ERA associated with an account.
*
* @param alias The alias to get the ERA for
* @param eraKey The ERA key to get
* @param cdrDate The date of the CDR
* @return The ERA value
* @throws ProcessingException
*/
public String getERA(String alias, String eraKey, long cdrDate) throws ProcessingException
{
Integer custId = null;
CustInfo tmpCustInfo;
AuditSegment tmpAuditSegment;
ValidityNode tmpValidityNode;
// See if we already have AuditSegID for this customer
if (aliasCache.containsKey(alias))
{
// get the start of the search tree
tmpValidityNode = aliasCache.get(alias);
// Now that we have the Validity Map, get the entry
while (tmpValidityNode != null)
{
if ((tmpValidityNode.validFrom <= cdrDate) &
(tmpValidityNode.validTo > cdrDate))
{
custId = tmpValidityNode.custId;
break;
}
// Move down the map
tmpValidityNode = tmpValidityNode.child;
}
if (custId == null)
{
// Otherwise write an error and ignore it
message = "Alias <" + alias + "> not found for time <" + cdrDate + ">. Lookup failed.";
throw new ProcessingException(message,getSymbolicName());
}
else
{
// get the customer for the alias
tmpCustInfo = custCache.get(custId);
//get the correct audit segment
tmpAuditSegment = tmpCustInfo.getBestAuditSegmentMatch(cdrDate);
if (tmpAuditSegment != null)
{
OpenRate.getOpenRateFrameworkLog().debug("Using audit segment <" + tmpAuditSegment.getAuditSegmentID() + "> for alias <" + alias + "> at time <" + cdrDate + ">");
return tmpAuditSegment.getERA(eraKey);
}
}
}
return null;
}
/**
* Return the value of the ERA associated with an account.
*
* @param tmpAuditSegment The audit segment to get the ERA from
* @param eraKey The key of the ERA to get
* @return The value of the ERA
*/
public String getERA(AuditSegment tmpAuditSegment, String eraKey)
{
return tmpAuditSegment.getERA(eraKey);
}
// -----------------------------------------------------------------------------
// -------------------- Start of data loading 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.
*
* @param ResourceName The name of the resource to load for
* @param CacheName The name of the cache to load for
* @throws InitializationException
*/
@Override
public void loadCache(String ResourceName, String CacheName)
throws InitializationException
{
String tmpFrequency;
// Do the parent processing first
super.loadCache(ResourceName, CacheName);
// load the update frequency
tmpFrequency = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName,
CacheName,
SERVICE_UPDATE_FREQUENCY,
"None");
if (!tmpFrequency.equalsIgnoreCase("None"))
{
// process it
processControlEvent(SERVICE_UPDATE_FREQUENCY,true,tmpFrequency);
}
}
/**
* load the data from a file
*
* @throws InitializationException
*/
@Override
public synchronized void loadDataFromFile() throws InitializationException
{
// Variable declarations
int auditSegsLoaded = 0;
int cpiLoaded = 0;
int aliasLoaded = 0;
int eraLoaded = 0;
int lineCounter = 0;
BufferedReader inFile;
String tmpFileRecord;
String[] recordFields;
SimpleDateFormat sdfInput = new SimpleDateFormat (internalDateFormat);
long audSegValidFrom;
int balGroup;
Integer custId = 0;
String tmpAudSegDate;
String tmpExtCustID;
String tmpCustID;
String tmpAuditSegID;
String tmpStrToDate;
String tmpStrFromDate;
String tmpProdName;
String tmpService;
String tmpSubID;
String tmpAlias;
long tmpToDate = CommonConfig.HIGH_DATE;
long tmpFromDate = CommonConfig.LOW_DATE;
String tmpCustToDate;
String tmpCustFromDate;
long custToDate = CommonConfig.HIGH_DATE;
long custFromDate = CommonConfig.LOW_DATE;
long AuditSegID;
// Log that we are starting the loading
OpenRate.getOpenRateFrameworkLog().info("Starting Customer Cache Loading from File");
// Try to open the file
try
{
inFile = new BufferedReader(new FileReader(cacheDataFile));
}
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();
lineCounter++;
if ((tmpFileRecord.startsWith("#")) |
tmpFileRecord.trim().equals(""))
{
// Comment line, ignore
}
else
{
recordFields = tmpFileRecord.split(";");
// Work on the different types of records in the file
if ( recordFields[0].equals("01") )
{
// Customer data - prepare the fields
tmpAuditSegID = recordFields[0];
tmpCustID = recordFields[1];
tmpExtCustID = recordFields[2];
tmpAudSegDate = recordFields[3];
tmpCustFromDate = recordFields[4];
tmpCustToDate = recordFields[5];
custId = Integer.parseInt(tmpCustID);
balGroup = Integer.parseInt(tmpCustID);
audSegValidFrom = Long.parseLong(tmpAudSegDate);
AuditSegID = Long.parseLong(tmpAuditSegID);
try
{
audSegValidFrom = sdfInput.parse(tmpAudSegDate).getTime()/1000;
custFromDate = sdfInput.parse(tmpCustFromDate).getTime()/1000;
custToDate = sdfInput.parse(tmpCustToDate).getTime()/1000;
}
catch (ParseException ex)
{
OpenRate.getOpenRateFrameworkLog().error("Date formats for record <" + tmpFileRecord + "> on line <" + lineCounter + "> are not correct. Data discarded." );
}
addAuditSegment(AuditSegID,custId,tmpExtCustID,balGroup,audSegValidFrom,custFromDate,custToDate);
auditSegsLoaded++;
// Update status for long operations
if ( (auditSegsLoaded % loadingLogNotificationStep) == 0)
{
OpenRate.getOpenRateFrameworkLog().info("Customer Map Data Loaded " + auditSegsLoaded + " Customer Records");
}
}
if (recordFields[0].equals("02"))
{
// Customer product - prepare the fields
tmpAuditSegID = recordFields[0];
tmpProdName = recordFields[1];
tmpSubID = recordFields[2];
tmpService = recordFields[3];
tmpStrFromDate = recordFields[4];
tmpStrToDate = recordFields[5];
AuditSegID = Long.parseLong(tmpAuditSegID);
try
{
tmpFromDate = sdfInput.parse(tmpStrFromDate).getTime()/1000;
tmpToDate = sdfInput.parse(tmpStrToDate).getTime()/1000;
}
catch (ParseException ex)
{
OpenRate.getOpenRateFrameworkLog().error("Date formats for record <" + tmpFileRecord + "> are not correct. Data discarded." );
}
addAuditedCPI(0, AuditSegID,tmpProdName,tmpSubID,tmpService,tmpFromDate,tmpToDate);
cpiLoaded++;
// Update status for long operations
if ( (cpiLoaded % loadingLogNotificationStep) == 0)
{
OpenRate.getOpenRateFrameworkLog().info("Customer Map Data Loaded " + cpiLoaded + " Product Records");
}
}
if ( recordFields[0].equals("05") )
{
tmpAuditSegID = recordFields[1];
tmpAlias = recordFields[2];
tmpCustID = recordFields[3];
tmpSubID = recordFields[4];
tmpStrFromDate = recordFields[5];
tmpStrToDate = recordFields[6];
AuditSegID = Long.parseLong(tmpAuditSegID);
try
{
tmpFromDate = sdfInput.parse(tmpStrFromDate).getTime()/1000;
tmpToDate = sdfInput.parse(tmpStrToDate).getTime()/1000;
}
catch (ParseException ex)
{
OpenRate.getOpenRateFrameworkLog().error("Date formats for record <" + tmpFileRecord + "> are not correct. Data discarded." );
}
// AuditSegID of 0 indicates that we do not allow update
addAlias(AuditSegID,tmpAlias,custId,tmpSubID,tmpFromDate,tmpToDate);
aliasLoaded++;
}
if ( recordFields[0].equals("06") )
{
tmpAuditSegID = recordFields[1];
tmpCustID = recordFields[2];
tmpSubID = recordFields[3];
tmpStrFromDate = recordFields[4];
tmpStrToDate = recordFields[5];
AuditSegID = Long.parseLong(tmpAuditSegID);
addAuditedERA(AuditSegID,recordFields[2],recordFields[3]);
eraLoaded++;
}
// Other types of record
// ...
}
}
}
catch (IOException ex)
{
OpenRate.getOpenRateFrameworkLog().fatal(
"Error reading input file <" + cacheDataFile +
"> in record <" + lineCounter + ">. IO Error.");
}
catch (ArrayIndexOutOfBoundsException ex)
{
OpenRate.getOpenRateFrameworkLog().fatal(
"Error reading input file <" + cacheDataFile +
"> in record <" + lineCounter + ">. Malformed Record.");
}
finally
{
try
{
inFile.close();
}
catch (IOException ex)
{
OpenRate.getOpenRateFrameworkLog().error("Error closing input file <" + cacheDataFile +
">", ex);
}
}
// set the last update time
lastUpdate = System.currentTimeMillis();
// finished
OpenRate.getOpenRateFrameworkLog().info(
"Customer Cache Data Loading completed. " + lineCounter +
" configuration lines loaded from <" + cacheDataFile +
">");
OpenRate.getOpenRateFrameworkLog().info("Alias Loaded: " + aliasLoaded);
OpenRate.getOpenRateFrameworkLog().info("CustomerAudit Segments Loaded: " + auditSegsLoaded);
OpenRate.getOpenRateFrameworkLog().info("Products Loaded: " + cpiLoaded);
OpenRate.getOpenRateFrameworkLog().info("ERAs Loaded: " + eraLoaded);
}
/**
* loadCache is called automatically on startup of the
* cache factory, as a result of implementing the CacheLoader
* interface.
*
* loadCache is also called when the cache is reloaded.
*
* @throws InitializationException
*/
@Override
public synchronized void loadDataFromDB() throws InitializationException
{
// Variable declarations
int aliasLoaded = 0;
int auditSegsLoaded = 0;
int cpiLoaded = 0;
int eraLoaded = 0;
String tmpAlias;
String tmpCustId;
String tmpSubId;
String tmpExtCustID;
String tmpProdName;
Integer custId;
long balGroup;
long auditSegID;
long aliasID;
long audSegValidFrom;
long fromDate;
long toDate;
long prodID;
long custToDate;
long custFromDate;
long modT;
String tmpService;
String tmpERAKey;
String tmpERAValue;
// Find the location of the zone configuration file
OpenRate.getOpenRateFrameworkLog().info("Starting Customer Cache Loading from DB");
// Try - finally wrapper
try
{
// Try to open the DS
JDBCcon = DBUtil.getConnection(cacheDataSourceName);
// Now prepare the statements
prepareStatements();
// set the where parameter to allow incremental loading
try
{
stmtAliasSelectQuery.clearParameters();
stmtAliasSelectQuery.setLong(1, lastAliasModT);
}
catch (SQLException ex)
{
message = "Error setting incremental ID for retieving customer " +
"alias data. SQL Error <" + ex.getMessage() + ">";
throw new InitializationException(message,ex,getSymbolicName());
}
// Execute the query
try
{
crs = stmtAliasSelectQuery.executeQuery();
}
catch (SQLException ex)
{
message = "Error executing SQL for retieving customer alias data." +
" SQL Error <" + ex.getMessage() + ">";
throw new InitializationException(message,ex,getSymbolicName());
}
try
{
// loop through the results for the customer alias cache
crs.next();
if (crs.getRow() == 0)
{
if (lastUpdate == 0)
{
// warn that the initial load did not return any results
OpenRate.getOpenRateFrameworkLog().warning("No results found for customer alias data");
}
}
else
{
do
{
aliasID = crs.getLong(1);
tmpAlias = crs.getString(2);
tmpCustId = crs.getString(3);
tmpSubId = crs.getString(4);
custFromDate = crs.getLong(5);
custToDate = crs.getLong(6);
modT = crs.getLong(7);
custId = Integer.parseInt(tmpCustId);
addAlias(aliasID,tmpAlias,custId,tmpSubId,custFromDate,custToDate);
aliasLoaded++;
// update the internal counter
if (modT > lastAliasModT)
{
lastAliasModT = modT;
}
if ((aliasLoaded % loadingLogNotificationStep) == 0)
{
message = "Customer Cache Alias Loading: <" + aliasLoaded +
"> configuration lines loaded for <" + getSymbolicName() + "> from <" +
cacheDataSourceName + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
}
} while (crs.next());
}
}
catch (SQLException ex)
{
message = "Error retreiving alias data. SQL Error <" +
ex.getMessage() + ">";
throw new InitializationException(message,ex,getSymbolicName());
}
OpenRate.getOpenRateFrameworkLog().info("Alias Loading completed. " + aliasLoaded +
" configuration lines loaded from <" + cacheDataSourceName + ">");
// set the where parameter to allow incremental loading
try
{
stmtAuditSelectQuery.clearParameters();
stmtAuditSelectQuery.setLong(1, lastAccountVerModT);
}
catch (SQLException ex)
{
message = "Error setting incremental ID for retieving customer " +
"audit data. SQL Error <" + ex.getMessage() + ">";
throw new InitializationException(message,ex,getSymbolicName());
}
// Execute the query
try
{
ars = stmtAuditSelectQuery.executeQuery();
}
catch (SQLException ex)
{
message = "Error executing SQL for retieving customer audit data. " +
"SQL Error <" + ex.getMessage() + ">";
throw new InitializationException(message,ex,getSymbolicName());
}
// loop through the results for the audit segment cache
try
{
// loop through the results for the audit segment cache
ars.next();
if (ars.getRow() == 0)
{
if (lastUpdate == 0)
{
// warn that the initial load did not return any results
OpenRate.getOpenRateFrameworkLog().warning("No results found for customer audit segment data");
}
}
else
{
do
{
auditSegID = ars.getLong(1);
tmpCustId = ars.getString(2);
balGroup = Long.parseLong(tmpCustId);
tmpExtCustID = ars.getString(3);
audSegValidFrom = ars.getLong(4);
custFromDate = ars.getLong(5);
custToDate = ars.getLong(6);
modT = ars.getLong(7);
custId = Integer.parseInt(tmpCustId);
addAuditSegment(auditSegID,custId,tmpExtCustID,balGroup,audSegValidFrom,custFromDate,custToDate);
auditSegsLoaded++;
// update the internal counter
if (modT > lastAccountVerModT)
{
lastAccountVerModT = modT;
}
if ((auditSegsLoaded % loadingLogNotificationStep) == 0)
{
message = "Customer Cache Audit Segment Loading: <" + auditSegsLoaded +
"> configuration lines loaded for <" + getSymbolicName() + "> from <" +
cacheDataSourceName + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
}
} while (ars.next());
}
}
catch (SQLException ex)
{
message = "Error retreiving audit data. SQL Error <" + ex.getMessage() + ">";
throw new InitializationException(message,ex,getSymbolicName());
}
OpenRate.getOpenRateFrameworkLog().info("Audit segment Loading completed. " + auditSegsLoaded +
" configuration lines loaded from <" + cacheDataSourceName + ">");
// set the where parameter to allow incremental loading
try
{
stmtProductSelectQuery.clearParameters();
stmtProductSelectQuery.setLong(1, lastProductModT);
}
catch (SQLException ex)
{
message = "Error setting incremental ID for retieving product data. " +
"SQL Error <" + ex.getMessage() + ">";
throw new InitializationException(message,ex,getSymbolicName());
}
// Execute the query
try
{
prs = stmtProductSelectQuery.executeQuery();
}
catch (SQLException ex)
{
message = "Error executing SQL for retieving customer product data. " +
"SQL Error <" + ex.getMessage() + ">";
throw new InitializationException(message,ex,getSymbolicName());
}
// loop through the results for the product cache
try
{
// loop through the results for the product cache
prs.next();
if (prs.getRow() == 0)
{
if (lastUpdate == 0)
{
// warn that the initial load did not return any results
OpenRate.getOpenRateFrameworkLog().warning("No results found for customer product data");
}
}
else
{
do
{
auditSegID = prs.getLong(1);
prodID = prs.getLong(2);
tmpProdName = prs.getString(3);
tmpSubId = prs.getString(4);
tmpService = prs.getString(5);
fromDate = prs.getLong(6);
toDate = prs.getLong(7);
modT = prs.getLong(8);
addAuditedCPI(auditSegID,prodID,tmpProdName,tmpSubId,tmpService,fromDate,toDate);
cpiLoaded++;
// update the internal counter
if (modT > lastProductModT)
{
lastProductModT = modT;
}
if ((cpiLoaded % loadingLogNotificationStep) == 0)
{
message = "Customer Cache Product Loading: <" + cpiLoaded +
"> configuration lines loaded for <" + getSymbolicName() + "> from <" +
cacheDataSourceName + ">";
OpenRate.getOpenRateFrameworkLog().info(message);
}
} while (prs.next());
}
}
catch (SQLException ex)
{
message = "Error retreiving product data. SQL Error <" + ex.getMessage() + ">";
throw new InitializationException(message,ex,getSymbolicName());
}
// set the where parameter to allow incremental loading
try
{
stmtERASelectQuery.clearParameters();
stmtERASelectQuery.setLong(1, lastERAModT);
}
catch (SQLException ex)
{
message = "Error setting incremental ID for retieving ERA data. " +
"SQL Error <" + ex.getMessage() + ">";
throw new InitializationException(message,ex,getSymbolicName());
}
// Execute the query
try
{
ers = stmtERASelectQuery.executeQuery();
}
catch (SQLException ex)
{
message = "Error executing SQL for retieving customer ERA data. " +
"SQL Error <" + ex.getMessage() + ">";
throw new InitializationException(message,ex,getSymbolicName());
}
// loop through the results for the era cache
try
{
// loop through the results for the era cache
ers.next();
if (ers.getRow() == 0)
{
if (lastUpdate == 0)
{
// warn that the initial load did not return any results
OpenRate.getOpenRateFrameworkLog().warning("No results found for customer ERA data");
}
}
else
{
do
{
auditSegID = ers.getLong(1);
tmpERAKey = ers.getString(2);
tmpERAValue = ers.getString(3);
modT = ers.getLong(4);
addAuditedERA(auditSegID,tmpERAKey,tmpERAValue);
eraLoaded++;
// update the internal counter
if (modT > lastERAModT)
{
lastERAModT = modT;
}
} while (ers.next());
}
}
catch (SQLException ex)
{
message = "Error retreiving ERA data. SQL Error <" + ex.getMessage() + ">";
throw new InitializationException(message,ex,getSymbolicName());
}
}
finally
{
// We pass through this block in any case - whether an Exception was
// thrown or not, to perform clean up
// Close down result sets
DBUtil.close(crs);
DBUtil.close(ars);
DBUtil.close(prs);
DBUtil.close(ers);
// close down statements
closeStatements();
// Close the DB connection
DBUtil.close(JDBCcon);
}
// set the last update time
lastUpdate = System.currentTimeMillis();
// finished
OpenRate.getOpenRateFrameworkLog().info(
"Customer Cache Data Loading completed from <" + cacheDataSourceName + ">");
OpenRate.getOpenRateFrameworkLog().info("Alias Loaded: " + aliasLoaded);
OpenRate.getOpenRateFrameworkLog().info("CustomerAudit Segments Loaded: " + auditSegsLoaded);
OpenRate.getOpenRateFrameworkLog().info("Products Loaded: " + cpiLoaded);
OpenRate.getOpenRateFrameworkLog().info("ERAs Loaded: " + eraLoaded);
}
/**
* Load the data from the defined Data Source Method
*
* @throws InitializationException
*/
@Override
public void loadDataFromMethod() throws InitializationException
{
throw new InitializationException("Not implemented yet",getSymbolicName());
}
/**
* Reset the cache
*/
@Override
public void clearCacheObjects()
{
// clear the cache
aliasCache.clear();
custCache.clear();
auditSegmentCache.clear();
// reset the incremental counters
lastAccountVerModT = 0;
lastAliasModT = 0;
lastERAModT = 0;
lastProductModT = 0;
}
/**
* This function sees if it is yet time to perform an update from the
* customer database, and if so, performs the update
*/
public void checkUpdate()
{
if (System.currentTimeMillis() > (lastUpdate + updateFrequency * 1000))
{
// perform the reload
if (CacheDataSourceType.equalsIgnoreCase("DB"))
{
try
{
loadDataFromDB();
}
catch (InitializationException ex)
{
OpenRate.getOpenRateFrameworkLog().error("Error performing incremental update: " + ex.getMessage());
}
}
else
{
OpenRate.getOpenRateFrameworkLog().error("Cannot perform incremental update from file source");
}
}
}
/**
* Write the internal memory of the customer to a file that can be used
* to debug the processing.
*
* @param alias The alias to dump for or "All" for all information
* @return The name of the dump file that was written
*/
public String DumpCacheData(String alias)
{
ArrayList<Integer> custIds;
BufferedWriter dumpWriter;
FileWriter fwriter = null;
File file;
String filename;
Iterator<String> aliasIter;
Iterator<Integer> IDIter;
Integer custId;
String aliasId;
String dumpString;
// Create the file name and open the file
filename = "CustomerInfo_" + alias + ".custinfo";
file = new File(filename);
try
{
if (file.createNewFile() == false)
{
OpenRate.getOpenRateFrameworkLog().error("Error creating file <" + filename + ">");
}
fwriter = new FileWriter(file);
}
catch (IOException ex)
{
OpenRate.getOpenRateFrameworkLog().error("Error opening dump file <" + filename + ">. message <" + ex.getMessage() +">");
}
dumpWriter = new BufferedWriter(fwriter);
// do the dumping
if (alias.equalsIgnoreCase("all"))
{
// dump all the alias data
dumpString = "==== Alias Information ====";
try
{
dumpWriter.write(dumpString);
dumpWriter.newLine();
}
catch (IOException ioe)
{
OpenRate.getOpenRateFrameworkLog().error("Error writing dump file", ioe);
}
aliasIter = aliasCache.keySet().iterator();
while (aliasIter.hasNext())
{
aliasId = aliasIter.next();
writeAliasInfo(aliasId,dumpWriter);
}
// Dump the customer data
dumpString = "==== Customer Information ====";
try
{
dumpWriter.newLine();
dumpWriter.write(dumpString);
dumpWriter.newLine();
}
catch (IOException ioe)
{
OpenRate.getOpenRateFrameworkLog().error("Error writing dump file", ioe);
}
IDIter = custCache.keySet().iterator();
while (IDIter.hasNext())
{
custId = IDIter.next();
writeCustInfo(custId,dumpWriter);
}
}
else
{
// dump the alias data
dumpString = "==== Alias Information ====";
try
{
dumpWriter.write(dumpString);
dumpWriter.newLine();
}
catch (IOException ioe)
{
OpenRate.getOpenRateFrameworkLog().error("Error writing dump file", ioe);
}
writeAliasInfo(alias,dumpWriter);
// Dump the customer data
dumpString = "==== Customer Information ====";
try
{
dumpWriter.newLine();
dumpWriter.write(dumpString);
dumpWriter.newLine();
}
catch (IOException ioe)
{
OpenRate.getOpenRateFrameworkLog().error("Error writing dump file", ioe);
}
// Get a list of all the Cust IDs that have used the alias
custIds = getAllCustId(alias);
IDIter = custIds.iterator();
while (IDIter.hasNext())
{
custId = IDIter.next();
writeCustInfo(custId,dumpWriter);
}
}
// close files
try
{
if (dumpWriter != null)
{
dumpWriter.close();
}
}
catch (IOException ioe)
{
OpenRate.getOpenRateFrameworkLog().error("Error closing dump file", ioe);
}
return filename;
}
/**
* Write the information for one customer, including all subordinate infomation
* (Audit Segments, Products and ERAs)
*
* @param custId The CustId to write
* @param dumpWriter The dump writer to write to
*/
private void writeCustInfo(Integer custId, BufferedWriter dumpWriter)
{
String DumpString;
CustInfo tmpCustInfo;
AuditSegment tmpAuditSegment;
ProductList tmpProductList;
int i;
int j;
CustProductInfo tmpCPI;
Iterator<String> ERAIter;
String ERAKey;
String FromDate;
String ToDate;
String EffDate;
try
{
DumpString = "Customer ID " + custId;
dumpWriter.write(DumpString);
dumpWriter.newLine();
tmpCustInfo = custCache.get(custId);
FromDate = fieldInterpreter.formatLongDate(tmpCustInfo.custValidFrom);
ToDate = fieldInterpreter.formatLongDate(tmpCustInfo.custValidTo);
DumpString = " Valid from " + tmpCustInfo.custValidFrom + " (" + FromDate +
") to " + tmpCustInfo.custValidTo + " (" + ToDate + ")";
dumpWriter.write(DumpString);
dumpWriter.newLine();
DumpString = " Balance group " + tmpCustInfo.balanceGroup;
dumpWriter.write(DumpString);
dumpWriter.newLine();
DumpString = " ExtCustID " + tmpCustInfo.ExternalCustId;
dumpWriter.write(DumpString);
dumpWriter.newLine();
// Now write the audit segment stuff
for (i = 0 ; i < tmpCustInfo.CustAudSegments.size() ; i++)
{
tmpAuditSegment = tmpCustInfo.CustAudSegments.get(i);
// Output the segment information
EffDate = fieldInterpreter.formatLongDate(tmpAuditSegment.getUTCSegmentValidFrom());
FromDate = fieldInterpreter.formatLongDate(tmpAuditSegment.getUTCAccountValidFrom());
ToDate = fieldInterpreter.formatLongDate(tmpAuditSegment.getUTCAccountValidTo());
DumpString = " Audit Segment ID " + tmpAuditSegment.getAuditSegmentID() +
" effective from " + tmpAuditSegment.getUTCSegmentValidFrom() + " (" +
EffDate + ") valid from " + tmpAuditSegment.getUTCAccountValidFrom() + " (" +
FromDate + ") to " + tmpAuditSegment.getUTCAccountValidFrom() + " (" + ToDate + ")";
dumpWriter.write(DumpString);
dumpWriter.newLine();
// Output the products
tmpProductList = tmpAuditSegment.getCustAudProds();
for ( j=0 ; j < tmpProductList.getProductCount() ; j++)
{
tmpCPI = tmpProductList.getProduct(j);
FromDate = fieldInterpreter.formatLongDate(tmpCPI.getUTCValidFrom());
ToDate = fieldInterpreter.formatLongDate(tmpCPI.getUTCValidTo());
DumpString = " Product ID " + tmpCPI.getProductID() + " for service " +
tmpCPI.getService() + " subscription " + tmpCPI.getSubID() +
" valid from " + tmpCPI.getUTCValidFrom() + " (" + FromDate +
") to " + tmpCPI.getUTCValidTo() + " (" + ToDate + ")";
dumpWriter.write(DumpString);
dumpWriter.newLine();
}
// Output the ERAs
ERAIter = tmpAuditSegment.getERAs().keySet().iterator();
while (ERAIter.hasNext())
{
ERAKey = ERAIter.next();
DumpString = " ERA " + ERAKey + " has value " + tmpAuditSegment.getERA(ERAKey);
dumpWriter.write(DumpString);
dumpWriter.newLine();
}
}
}
catch (IOException ex)
{
OpenRate.getOpenRateFrameworkLog().error("Error writing to dump file. message <" + ex.getMessage() + ">");
}
}
/**
* Write the information for an alias, which may be a single custId, or multiple
* in the case that the alias has been associated with various CustIds over
* time
*
* @param CustId The alias to write
* @param dumpWriter The dump writer to write to
*/
private void writeAliasInfo(String aliasId, BufferedWriter dumpWriter)
{
String DumpString;
ValidityNode tmpValidityNode;
try
{
// Write the header
DumpString = "Alias ID " + aliasId;
dumpWriter.write(DumpString);
dumpWriter.newLine();
// Write the information for the alias history
tmpValidityNode = aliasCache.get(aliasId);
while (tmpValidityNode != null)
{
String FromDate = fieldInterpreter.formatLongDate(tmpValidityNode.validFrom);
String ToDate = fieldInterpreter.formatLongDate(tmpValidityNode.validTo);
DumpString = " Associated with Cust ID " + tmpValidityNode.custId +
" from " + tmpValidityNode.validFrom + " (" + FromDate +
") to " + tmpValidityNode.validTo + " (" + ToDate +
") for Subscription " + tmpValidityNode.subId;
dumpWriter.write(DumpString);
dumpWriter.newLine();
// Move down the map
tmpValidityNode = tmpValidityNode.child;
}
}
catch (IOException ex)
{
OpenRate.getOpenRateFrameworkLog().error("Error writing to dump file. message <" + ex.getMessage() + ">");
}
}
// -----------------------------------------------------------------------------
// ---------------- Start of data base data layer functions --------------------
// -----------------------------------------------------------------------------
/**
* get the select statement(s). Implemented as a separate function so that it can
* be overwritten in implementation classes. By default the cache picks up the
* statement with the name "SelectStatement".
*
* @param ResourceName The name of the resource to load for
* @param CacheName The name of the cache to load for
* @return True if the statements were found, otherwise false
* @throws InitializationException
*/
@Override
protected boolean getDataStatements(String ResourceName, String CacheName)
throws InitializationException
{
// Get the Select statement
aliasSelectQuery = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName,
CacheName,
"AliasSelectStatement",
"None");
if (aliasSelectQuery.equalsIgnoreCase("None"))
{
message = "<AliasSelectStatement> for <" + getSymbolicName() + "> missing.";
throw new InitializationException(message,getSymbolicName());
}
auditSelectQuery = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName,
CacheName,
"AuditSegmentSelectStatement",
"None");
if (auditSelectQuery.equalsIgnoreCase("None"))
{
message = "<AuditSegmentSelectStatement> for <" + getSymbolicName() + "> missing.";
throw new InitializationException(message,getSymbolicName());
}
productSelectQuery = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName,
CacheName,
"ProductSelectStatement",
"None");
if (productSelectQuery.equalsIgnoreCase("None"))
{
message = "<ProductSelectStatement> for <" + getSymbolicName() + "> missing.";
throw new InitializationException(message,getSymbolicName());
}
eraSelectQuery = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName,
CacheName,
"ERASelectStatement",
"None");
if (eraSelectQuery.equalsIgnoreCase("None"))
{
message = "<ERASelectStatement> for <" + getSymbolicName() + "> missing.";
throw new InitializationException(message,getSymbolicName());
}
if (aliasSelectQuery.equals("None") |
auditSelectQuery.equals("None") |
productSelectQuery.equals("None")|
eraSelectQuery.equals("None"))
{
return false;
}
else
{
return true;
}
}
/**
* PrepareStatements creates the statements from the SQL expressions
* so that they can be run as needed.
*
* @throws InitializationException
*/
@Override
protected void prepareStatements() throws InitializationException
{
try
{
// prepare the SQL for the Alias Statement
stmtAliasSelectQuery = JDBCcon.prepareStatement(aliasSelectQuery,ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
}
catch(SQLException ex)
{
message = "Error preparing the statement " + aliasSelectQuery + "SQL Error:" + ex.getMessage();
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,ex,getSymbolicName());
}
try
{
// prepare the SQL for the Audit Statement
stmtAuditSelectQuery = JDBCcon.prepareStatement(auditSelectQuery, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
}
catch(SQLException ex)
{
message = "Error preparing the statement <" + auditSelectQuery + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,ex,getSymbolicName());
}
try
{
// prepare the SQL for the Product Statement
stmtProductSelectQuery = JDBCcon.prepareStatement(productSelectQuery, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
}
catch(SQLException ex)
{
message = "Error preparing the statement <" + productSelectQuery + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,ex,getSymbolicName());
}
try
{
// prepare the SQL for the ERA Statement
stmtERASelectQuery = JDBCcon.prepareStatement(eraSelectQuery, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
}
catch(SQLException ex)
{
message = "Error preparing the statement <" + eraSelectQuery + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
throw new InitializationException(message,ex,getSymbolicName());
}
}
/**
* closeStatements closes down the the statements from the SQL expressions.
*
*/
protected void closeStatements()
{
// prepare the SQL for the Alias Statement
DBUtil.close(stmtAliasSelectQuery);
// prepare the SQL for the Audit Statement
DBUtil.close(stmtAuditSelectQuery);
// prepare the SQL for the Product Statement
DBUtil.close(stmtProductSelectQuery);
// prepare the SQL for the ERA Statement
DBUtil.close(stmtERASelectQuery);
}
// -----------------------------------------------------------------------------
// ------------- 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.
*
*/
@Override
public void registerClientManager() throws InitializationException
{
super.registerClientManager();
//Register services for this Client
ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_UPDATE_FREQUENCY, ClientManager.PARAM_DYNAMIC_SYNC);
ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_DUMP_INFO, 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_UPDATE_FREQUENCY))
{
if (Parameter.equalsIgnoreCase(""))
{
// return the current value
return String.valueOf(updateFrequency);
}
else
{
// try to update
try
{
updateFrequency = Integer.parseInt(Parameter);
}
catch (NumberFormatException nfe)
{
return "<" + Parameter + "> is not a valid integer value";
}
ResultCode = 0;
}
}
if (Command.equalsIgnoreCase(SERVICE_DUMP_INFO))
{
if (Parameter.equalsIgnoreCase("All"))
{
// dump all cache data
FileName = DumpCacheData("All");
return "Wrote file <" + FileName + ">";
}
else if (Parameter.length() > 0)
{
// dump only a specific alias
FileName = DumpCacheData(Parameter);
return "Wrote file <" + FileName + "> for alias <" + Parameter + ">";
}
else
{
// We did not get a parameter
return "Operation <" + SERVICE_DUMP_INFO + "> requires a parameter 'All' or a specific Alias";
}
}
if (ResultCode == 0)
{
OpenRate.getOpenRateFrameworkLog().debug(LogUtil.LogECICacheCommand(getSymbolicName(), Command, Parameter));
return "OK";
}
else
{
return super.processControlEvent(Command, Init, Parameter);
}
}
}