/* ==================================================================== * 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.db.DBUtil; import OpenRate.exception.InitializationException; import OpenRate.record.RateMapEntry; import OpenRate.utils.PropertyUtils; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; /** * Please * <a target='new' href='http://www.open-rate.com/wiki/index.php?title=RUM_Rate_Cache'>click * here</a> to go to wiki page. * <br> * <p> * This class extends the basic rating scheme found in the "RateCache" module to * implement a rating scheme based on full mappable RUM (rateable Usage Metric) * model for rating records according to a tier and beat model, as well as * impacting multiple resources using multiple RUMs. * * The data is read in from a configuration file or database, and contains two * distinct sorts of information: - Price Model Map. This describes the price * models - RUM Map. This describes the price models to apply, the RUMs to read * and the resources to impact. * * For the data source type "File", the data for the Price Model Map will be * read from the file you give under "PriceModelDataFile". The RUM Map will be * read from the file that you give under "RUMMapDataFile". * * For the data source type "DB", the data for the Price Model Map will be read * from the query you give under "PriceModelStatement". The RUM Map will be read * from the query that you give under "RUMMapStatement". * * In either case, the data that is read is: * * Price Model Map --------------- * PriceModel;Step;TierFrom;TierTo;Beat;Factor;ChargeBase * * Where: 'PriceModel' is the identifier for a price model 'Step' is the number * of the tier, starting from 1 and incrementing for each tier to be evaluated * 'TierFrom' is the start of the tier, usually tiers will start from 1 'TierTo' * is the end of the tier 'Beat' is the granularity of the rating (should be an * exact fraction of the tier) 'Factor' is the cost of each "charge base" number * of units in this tier 'ChargeBase' is the number of units for which the cost * factor has been defined. This lets you define prices as minutes (ChargeBase = * 60), but still rate on a per second basis (Beat = 1). 'StartTime' is the * start of the validity of this step, allowing multiple validity periods for * each step. A value of 0 in this field means that the step will be valid for * ever 'EndTime' is the end of the validity of this step, allowing multiple * validity periods for each step * * RUM Map --------------- PriceGroup;PriceModel;RUM;Resource;RUMType;ResCtr * * Where: 'PriceGroup' is a unique code for the Price Model Group. All price * models within the group will be executed 'PriceModel' is a unique code for * the PriceModel 'RUM' is the name of the RUM to be used by the price model * 'Resource' is the resource to impact 'RUMType' is the type of rating that * should be performed on the RUM, and can be one of the following: 'Flat' - * will return a simple RUM * Price, without tiers or beats 'Tiered' - will * evaluate all tiers, rating the portion of the RUM that lies within the tier * individually 'Threshold' - will rate all of the RUM in the tier that the RUM * lies in 'Event' - will return a fixed value regardless of the RUM value * 'ResCtr' is the counter to be impacted for this resource * * @author i.sparkes */ public class RUMRateCache extends AbstractSyncLoaderCache { /** * RUM Map entry */ public class RUMMapEntry { /** * RUMType indicates the type of rating we are to use */ public int RUMType; /** * The name of the price model */ public String PriceModel; /** * The Rateable Usage Metric - indicates what value we are rating */ public String RUM; /** * The resource that we are to impact */ public String Resource; /** * The counter ID for the resource to impact */ public int ResourceCounter; /** * If this is true, we reduce the amount of RUM to rate after rating */ public boolean ConsumeRUM = false; } /** * This stores all the cacheable data necessary for the definition of the rate * plans. */ protected HashMap<String, ArrayList<RateMapEntry>> PriceModelCache; /** * This holds the RUM map */ protected HashMap<String, ArrayList<RUMMapEntry>> RUMMapCache; /** * these are the statements that we have to prepare to be able to get records * once and only once */ protected static String PriceModelDataSelectQuery; /** * these are the prepared statements */ protected static PreparedStatement StmtPriceModelDataSelectQuery; /** * these are the statements that we have to prepare to be able to get records * once and only once */ protected static String RUMMapDataSelectQuery; /** * these are the prepared statements */ protected static PreparedStatement StmtRUMMapDataSelectQuery; /** * this is the name of the file that holds the RUM Map */ protected static String RUMMapDataFile; /** * this is the name of the file that holds price models */ protected static String PriceModelDataFile; // ----------------------------------------------------------------------------- // ------------------ 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. * * @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 { int initialObjectSize = 1000; String tmpInitOjectSize = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName, CacheName, "InitialObjectSize", "1000"); try { initialObjectSize = Integer.parseInt(tmpInitOjectSize); } catch (NumberFormatException nfe) { message = "Could not parse initial object size <" + initialObjectSize + "> for cache <" + getSymbolicName() + ">"; throw new InitializationException(message, getSymbolicName()); } // inform the user about the start of the price model phase OpenRate.getOpenRateFrameworkLog().debug("Setting initial hash map size to <" + initialObjectSize + "> for cache <" + getSymbolicName() + ">"); PriceModelCache = new HashMap<>(initialObjectSize); RUMMapCache = new HashMap<>(initialObjectSize); // Do the parent initialisation super.loadCache(ResourceName, CacheName); } // ----------------------------------------------------------------------------- // ----------------------- Start of custom functions --------------------------- // ----------------------------------------------------------------------------- /** * Add a value into the RateCache, defining the RateMapEntry result value that * should be returned in the case of a match. A PriceModel is therefore * defined as a group of RateMapEntries, that make up a whole rate map. * * @param priceModel The price model name to add * @param step The tier number (starting from 1) to enable multiple tiers * @param from The start of the tier * @param to The end of the tier * @param beat The charging granularity * @param factor The value to charge for each beat * @param chargeBase The base amount to charge the factor for * @param startTime The start time of the validity * @throws InitializationException */ public void addPriceModel( String priceModel, int step, double from, double to, double beat, double factor, double chargeBase, long startTime) throws InitializationException { ArrayList<RateMapEntry> tmpRateCache; RateMapEntry tmpRMEntry; RateMapEntry helperRMEntry; int i; boolean inserted = false; // Validate the beat if (beat <= 0) { message = "Beat in model <" + priceModel + "> and step number <" + step + "> is invalid <" + beat + "> in module <" + getSymbolicName() + ">"; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } // See if we already have the cache object for this price if (!PriceModelCache.containsKey(priceModel)) { // Create the new PriceModel object tmpRateCache = new ArrayList<>(); PriceModelCache.put(priceModel, tmpRateCache); // Add it as the first element in the ArrayList tmpRMEntry = new RateMapEntry(); tmpRMEntry.setStep(step); tmpRMEntry.setFrom(from); tmpRMEntry.setTo(to); tmpRMEntry.setBeat(beat); tmpRMEntry.setFactor(factor); tmpRMEntry.setChargeBase(chargeBase); tmpRMEntry.setStartTime(startTime); // so add the entry to the new map. No need to order it, it is the first tmpRateCache.add(tmpRMEntry); } else { // Otherwise just add it to the existing rate model tmpRateCache = PriceModelCache.get(priceModel); // Add the new entry tmpRMEntry = new RateMapEntry(); tmpRMEntry.setStep(step); tmpRMEntry.setFrom(from); tmpRMEntry.setTo(to); tmpRMEntry.setBeat(beat); tmpRMEntry.setFactor(factor); tmpRMEntry.setChargeBase(chargeBase); tmpRMEntry.setStartTime(startTime); // Add the object to the ArrayList for (i = 0; i < tmpRateCache.size(); i++) { // if it is a later step if (tmpRateCache.get(i).getStep() > step) { // add a null element tmpRateCache = insertElementAt(tmpRateCache, tmpRMEntry, i); inserted = true; break; } else if (tmpRateCache.get(i).getStep() == step) { // if it is a later time version of the same step // see if it goes before or after the current one if (tmpRateCache.get(i).getStartTime() > startTime) { // inserting helperRMEntry = tmpRateCache.get(i); tmpRMEntry.setChild(helperRMEntry); tmpRateCache.set(i, tmpRMEntry); } else if (tmpRateCache.get(i).getStartTime() < startTime) { // appending tmpRateCache.get(i).setChild(tmpRMEntry); } else { // cannot have two steps with the same start date message = "Two steps in model <" + priceModel + "> and step number <" + step + "> have the same start date <" + startTime + "> in module <" + getSymbolicName() + ">"; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } } } if (inserted == false) { // we did not insert it, so add it tmpRateCache.add(tmpRMEntry); } } } /** * Get a value from the RateCache. The processing based on the result returned * here is evaluated in the twinned processing class, in order to reduce the * load on the main framework thread. * * @param key The price model to recover * @return The price model structure containing all of the tiers */ public ArrayList<RateMapEntry> getPriceModel(String key) { ArrayList<RateMapEntry> tmpEntry; // Get the rate plan tmpEntry = PriceModelCache.get(key); // and return it return tmpEntry; } /** * Add a value into the price map cache. * * @param PriceGroup The name of the price group to map * @param PriceModel The name of the price model to add to the map * @param RUM The name of the RUM to apply for this price model * @param Resource The name of the Resource to impact * @param RUMType The type of RUM for this impact * @param ResourceCounter The counter ID for the resource * @throws OpenRate.exception.InitializationException */ public void addRUMMap(String PriceGroup, String PriceModel, String RUM, String Resource, String RUMType, String ResourceCounter) throws InitializationException { ArrayList<RUMMapEntry> tmpRUMMapCache; RUMMapEntry tmpRMEntry; // See if we already have the cache object for this price if (!RUMMapCache.containsKey(PriceGroup)) { // Create the new PriceModel object tmpRUMMapCache = new ArrayList<>(); RUMMapCache.put(PriceGroup, tmpRUMMapCache); tmpRMEntry = new RUMMapEntry(); tmpRMEntry.PriceModel = PriceModel; tmpRMEntry.RUM = RUM; tmpRMEntry.Resource = Resource; tmpRMEntry.ResourceCounter = Integer.parseInt(ResourceCounter); if (RUMType.equalsIgnoreCase("flat")) { tmpRMEntry.RUMType = 1; } else if (RUMType.equalsIgnoreCase("tiered")) { tmpRMEntry.RUMType = 2; } else if (RUMType.equalsIgnoreCase("threshold")) { tmpRMEntry.RUMType = 3; } else if (RUMType.equalsIgnoreCase("event")) { tmpRMEntry.RUMType = 4; } else { message = "Unknown rating type <" + RUMType + ">"; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } // so add the entry to the new map. No need to order it, it is the first tmpRUMMapCache.add(tmpRMEntry); } else { // Otherwise just add it to the existing rate model tmpRUMMapCache = RUMMapCache.get(PriceGroup); // Add the new entry tmpRMEntry = new RUMMapEntry(); tmpRMEntry.PriceModel = PriceModel; tmpRMEntry.RUM = RUM; tmpRMEntry.Resource = Resource; tmpRMEntry.ResourceCounter = Integer.parseInt(ResourceCounter); if (RUMType.equalsIgnoreCase("flat")) { tmpRMEntry.RUMType = 1; } else if (RUMType.equalsIgnoreCase("tiered")) { tmpRMEntry.RUMType = 2; } else if (RUMType.equalsIgnoreCase("threshold")) { tmpRMEntry.RUMType = 3; } else if (RUMType.equalsIgnoreCase("event")) { tmpRMEntry.RUMType = 4; } else { message = "Unknown rating type <" + RUMType + ">"; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } // Add the object to the vector tmpRUMMapCache.add(tmpRMEntry); } } /** * Get a value from the RateCache. The processing based on the result returned * here is evaluated in the twinned processing class, in order to reduce the * load on the main framework thread. * * @param key The identifier for the RUM map to recover * @return The RUM map containing all of the pricemodel-RUM-Resource * combinations */ public ArrayList<RUMMapEntry> getRUMMap(String key) { ArrayList<RUMMapEntry> tmpEntry; // Get the rate plan tmpEntry = RUMMapCache.get(key); // and return it return tmpEntry; } // ----------------------------------------------------------------------------- // ------------------ Start of inherited loading functions --------------------- // ----------------------------------------------------------------------------- /** * Load the data from the defined file * * @throws OpenRate.exception.InitializationException */ @Override public void loadDataFromFile() throws InitializationException { // Variable declarations int RatesLoaded = 0; BufferedReader inFile; String tmpFileRecord; String[] RateFields; double tmpFrom; double tmpTo; double tmpBeat; double tmpFactor; double tmpChargeBase; int tmpTier; int MapsLoaded = 0; String PriceModel; String tmpGroup; String tmpModel; String tmpRUM; String tmpResource; String tmpRUMType; String tmpResCtr; String tmpStringStartTime = null; long tmpStartTime = CommonConfig.LOW_DATE; // default = low date // ****** perform the loading of the raw price models ****** // Find the location of the configuration file OpenRate.getOpenRateFrameworkLog().info("Starting Price Model Data Loading from file for <" + getSymbolicName() + ">"); // Try to open the file try { inFile = new BufferedReader(new FileReader(PriceModelDataFile)); } catch (FileNotFoundException fnfe) { message = "Not able to read file : <" + PriceModelDataFile + ">. message = <" + fnfe.getMessage() + ">"; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } // inform the user about the start of the price model phase OpenRate.getOpenRateFrameworkLog().info("Starting Price Model Data Loading from file for <" + getSymbolicName() + ">"); // File open, now get the stuff try { while (inFile.ready()) { tmpFileRecord = inFile.readLine(); if ((tmpFileRecord.startsWith("#")) | tmpFileRecord.trim().equals("")) { // Comment line, ignore } else { RatesLoaded++; RateFields = tmpFileRecord.split(";"); // check we have something we can use - either we expect 7 fields (no // date defined) or 9 fields (date defined). Everything else is BAD if ((RateFields.length == 7) | (RateFields.length == 8)) { // Prepare and add the line PriceModel = RateFields[0]; tmpTier = Integer.valueOf(RateFields[1]); tmpFrom = Double.valueOf(RateFields[2]); tmpTo = Double.valueOf(RateFields[3]); tmpBeat = Double.valueOf(RateFields[4]); tmpFactor = Double.valueOf(RateFields[5]); tmpChargeBase = Double.valueOf(RateFields[6]); // if we have the date, load it, otherwise use the default if (RateFields.length == 8) { tmpStringStartTime = RateFields[7]; tmpStartTime = fieldInterpreter.convertInputDateToUTC(tmpStringStartTime); } if (tmpChargeBase == 0) { message = "Error in price model <" + PriceModel + "> in module <" + getSymbolicName() + ">. Charge base cannot be 0."; OpenRate.getOpenRateFrameworkLog().fatal(message); throw new InitializationException(message, getSymbolicName()); } addPriceModel(PriceModel, tmpTier, tmpFrom, tmpTo, tmpBeat, tmpFactor, tmpChargeBase, tmpStartTime); } else { // Not a valid number of fields message = "Invalid number of fields in price map loading for module <" + getSymbolicName() + "> at line <" + RatesLoaded + ">. Expecting <7> or <8>, but got <" + RateFields.length + ">. Line was <" + tmpFileRecord + ">"; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } } } } catch (IOException ex) { message = "Error reading input file <" + PriceModelDataFile + "> in record <" + RatesLoaded + ">. IO Error."; OpenRate.getOpenRateFrameworkLog().fatal(message); throw new InitializationException(message, getSymbolicName()); } catch (ArrayIndexOutOfBoundsException ex) { message = "Error reading input file <" + PriceModelDataFile + "> in record <" + RatesLoaded + ">. Malformed Record."; OpenRate.getOpenRateFrameworkLog().fatal(message); throw new InitializationException(message, getSymbolicName()); } catch (ParseException pe) { message = "Error converting date from <" + PriceModelDataFile + "> in record <" + RatesLoaded + ">. Unexpected date value <" + tmpStringStartTime + ">"; OpenRate.getOpenRateFrameworkLog().fatal(message); throw new InitializationException(message, getSymbolicName()); } finally { try { inFile.close(); } catch (IOException ex) { message = "Error closing input file <" + PriceModelDataFile + ">. message = <" + ex.getMessage() + ">"; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } } OpenRate.getOpenRateFrameworkLog().info( "Price Model Data Loading completed. " + RatesLoaded + " configuration lines loaded from <" + getSymbolicName() + ">"); // ****** perform the loading of the model descriptors ****** // Find the location of the configuration file OpenRate.getOpenRateFrameworkLog().info("Starting Price Group Data Loading from file for <" + getSymbolicName() + ">"); // Try to open the file try { inFile = new BufferedReader(new FileReader(RUMMapDataFile)); } catch (FileNotFoundException fnfe) { message = "Not able to read file : <" + RUMMapDataFile + ">. message = <" + fnfe.getMessage() + ">"; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } // File open, now get the stuff try { while (inFile.ready()) { tmpFileRecord = inFile.readLine(); if ((tmpFileRecord.startsWith("#")) | tmpFileRecord.trim().equals("")) { // Comment line, ignore } else { MapsLoaded++; RateFields = tmpFileRecord.split(";"); if (RateFields.length == 6) { // Prepare and add the line tmpGroup = RateFields[0]; tmpModel = RateFields[1]; tmpRUM = RateFields[2]; tmpResource = RateFields[3]; tmpRUMType = RateFields[4]; tmpResCtr = RateFields[5]; addRUMMap(tmpGroup, tmpModel, tmpRUM, tmpResource, tmpRUMType, tmpResCtr); } else { // Not a valid number of fields message = "Invalid number of fields in price map loading for module <" + getSymbolicName() + ">. Expecting <6>, but got <" + RateFields.length + ">. Line was <" + tmpFileRecord + ">"; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } } } } catch (IOException ex) { message = "Error reading input file <" + RUMMapDataFile + "> in record <" + MapsLoaded + ">. IO Error."; OpenRate.getOpenRateFrameworkLog().fatal(message); throw new InitializationException(message, getSymbolicName()); } catch (ArrayIndexOutOfBoundsException ex) { message = "Error reading input file <" + RUMMapDataFile + "> in record <" + MapsLoaded + ">. Malformed Record."; OpenRate.getOpenRateFrameworkLog().fatal(message); throw new InitializationException(message, getSymbolicName()); } finally { try { inFile.close(); } catch (IOException ex) { message = "Error closing input file <" + RUMMapDataFile + ">. message = <" + ex.getMessage() + ">"; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } } OpenRate.getOpenRateFrameworkLog().info( "Price Group Data Loading completed. " + MapsLoaded + " configuration lines loaded from <" + RUMMapDataFile + ">"); } /** * Load the data from the defined Data Source * * @throws OpenRate.exception.InitializationException */ @Override public void loadDataFromDB() throws InitializationException { int mapsLoaded = 0; String tmpGroup = null; String tmpModel = null; String tmpRUM = null; String tmpResource = null; String tmpRUMType = null; String tmpResCtr = null; int ratesLoaded = 0; int columns; String priceModel; double tmpFrom; double tmpTo; double tmpBeat; double tmpFactor; int tmpTier; double tmpChargeBase; long tmpStartTime = CommonConfig.LOW_DATE; // default = low date String tmpStringStartTime = null; // Find the location of the configuration file OpenRate.getOpenRateFrameworkLog().info("Starting RUM Rate Cache Loading from DB for <" + getSymbolicName() + ">"); // Try to open the DS JDBCcon = DBUtil.getConnection(cacheDataSourceName); // ****** perform the loading of the raw price models ****** // Now prepare the statements prepareStatements(); // inform the user about the start of the price model phase OpenRate.getOpenRateFrameworkLog().info("Starting Price Model Data Loading from DB for <" + getSymbolicName() + ">"); // Execute the query try { mrs = StmtPriceModelDataSelectQuery.executeQuery(); columns = mrs.getMetaData().getColumnCount(); } catch (SQLException ex) { message = "Error performing SQL for retieving Price Model Data for <" + getSymbolicName() + ">. SQL Error = <" + ex.getMessage() + ">"; OpenRate.getOpenRateFrameworkLog().fatal(message); throw new InitializationException(message, getSymbolicName()); } // check we have something we can use - either we expect 7 fields (no // date defined) or 8 fields (date defined). Everything else is BAD if ((columns == 7) | (columns == 8)) { // loop through the results for the price model try { mrs.beforeFirst(); while (mrs.next()) { ratesLoaded++; priceModel = mrs.getString(1); tmpTier = mrs.getInt(2); tmpFrom = mrs.getDouble(3); tmpTo = mrs.getDouble(4); tmpBeat = mrs.getDouble(5); tmpFactor = mrs.getDouble(6); tmpChargeBase = mrs.getDouble(7); // if we have the date, load it, otherwise use the default if (columns == 8) { tmpStringStartTime = mrs.getString(8); tmpStartTime = fieldInterpreter.convertInputDateToUTC(tmpStringStartTime); } if (tmpChargeBase == 0) { // cannot have a 0 charge base - exception message = "Error in price model <" + priceModel + "> in module <" + getSymbolicName() + ">. Charge base cannot be 0."; OpenRate.getOpenRateFrameworkLog().fatal(message); throw new InitializationException(message, getSymbolicName()); } // Add the map addPriceModel(priceModel, tmpTier, tmpFrom, tmpTo, tmpBeat, tmpFactor, tmpChargeBase, tmpStartTime); } } catch (SQLException ex) { message = "Error opening Price Model Data for <" + getSymbolicName() + ">. SQL Error = <" + ex.getMessage() + ">"; OpenRate.getOpenRateFrameworkLog().fatal(message); throw new InitializationException(message, getSymbolicName()); } catch (ParseException pe) { message = "Error converting date from <" + getSymbolicName() + "> in record <" + ratesLoaded + ">. Unexpected date value <" + tmpStringStartTime + ">"; OpenRate.getOpenRateFrameworkLog().fatal(message); throw new InitializationException(message, getSymbolicName()); } } else { // Not a valid number of fields message = "Invalid number of fields in price map loading for module <" + getSymbolicName() + ">. Expecting <7> or <8>, but got <" + columns + ">."; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } // Close down stuff DBUtil.close(mrs); DBUtil.close(StmtPriceModelDataSelectQuery); OpenRate.getOpenRateFrameworkLog().info( "Price Model Data Loading completed. " + ratesLoaded + " configuration lines loaded from <" + getSymbolicName() + ">"); // ****** perform the loading of the model descriptors ****** // Find the location of the configuration file OpenRate.getOpenRateFrameworkLog().info("Starting Price Group Data Loading from DB for <" + getSymbolicName() + ">"); // Execute the query try { mrs = StmtRUMMapDataSelectQuery.executeQuery(); columns = mrs.getMetaData().getColumnCount(); } catch (SQLException ex) { message = "Error performing SQL for retieving Price Group Data for <" + getSymbolicName() + ">. SQL Error = <" + ex.getMessage() + ">"; OpenRate.getOpenRateFrameworkLog().fatal(message); throw new InitializationException(message, getSymbolicName()); } // check we have something we can use - we expect 7 fields. Everything // else is BAD if (columns == 6) { // loop through the results for the price model try { mrs.beforeFirst(); while (mrs.next()) { mapsLoaded++; tmpGroup = mrs.getString(1); tmpModel = mrs.getString(2); tmpRUM = mrs.getString(3); tmpResource = mrs.getString(4); tmpRUMType = mrs.getString(5); tmpResCtr = mrs.getString(6); // Add the map addRUMMap(tmpGroup, tmpModel, tmpRUM, tmpResource, tmpRUMType, tmpResCtr); } } catch (SQLException Sex) { message = "Error opening Price Group Data for <" + getSymbolicName() + ">. SQL Error = <" + Sex.getMessage() + ">"; OpenRate.getOpenRateFrameworkLog().fatal(message); throw new InitializationException(message, getSymbolicName()); } catch (NullPointerException npe) { message = "Null value loading Price Group Data for <" + getSymbolicName() + ">. Group <" + tmpGroup + ">, Model <" + tmpModel + ">, RUM <" + tmpRUM + ">, Resource <" + tmpResource + ">, RUM Type <" + tmpRUMType + ">, Step <" + tmpResCtr + ">"; OpenRate.getOpenRateFrameworkLog().fatal(message); throw new InitializationException(message, getSymbolicName()); } } else { // Not a valid number of fields message = "Invalid number of fields in rum map loading for module <" + getSymbolicName() + ">. Expecting <6>, but got <" + columns + ">."; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } // Close down stuff DBUtil.close(mrs); DBUtil.close(StmtPriceModelDataSelectQuery); DBUtil.close(JDBCcon); OpenRate.getOpenRateFrameworkLog().info( "Price Group Data Loading completed. " + mapsLoaded + " configuration lines loaded from <" + getSymbolicName() + ">"); } /** * Load the data from the defined Data Source Method * * @throws OpenRate.exception.InitializationException */ @Override public void loadDataFromMethod() throws InitializationException { throw new InitializationException("Not implemented yet", getSymbolicName()); } /** * Clear down the cache contents in the case that we are ordered to reload */ @Override public void clearCacheObjects() { // clear the price model cache PriceModelCache.clear(); // clear the RUM map cache RUMMapCache.clear(); } // ----------------------------------------------------------------------------- // ---------------- Start of data base data layer functions -------------------- // ----------------------------------------------------------------------------- /** * Get the data files that we are going to be reading, when reading from * "File" data source types * * @return true if the configuration is good, otherwise false. * @throws OpenRate.exception.InitializationException */ @Override protected boolean getDataFiles(String ResourceName, String CacheName) throws InitializationException { // Get the Select statement RUMMapDataFile = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName, CacheName, "RUMMapDataFile", "None"); // Get the Select statement PriceModelDataFile = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName, CacheName, "PriceModelDataFile", "None"); if (RUMMapDataFile.equals("None") | PriceModelDataFile.equals("None")) { return false; } else { return true; } } /** * get the select statement(s). Implemented as a separate function so that it * can be overwritten in implementation classes. * * @return true if the configuration is good, otherwise false. * @throws OpenRate.exception.InitializationException */ @Override protected boolean getDataStatements(String ResourceName, String CacheName) throws InitializationException { // Get the Select statement PriceModelDataSelectQuery = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName, CacheName, "PriceModelStatement", "None"); // Get the Select statement RUMMapDataSelectQuery = PropertyUtils.getPropertyUtils().getDataCachePropertyValueDef(ResourceName, CacheName, "RUMMapStatement", "None"); if (PriceModelDataSelectQuery.equals("None") | RUMMapDataSelectQuery.equals("None")) { return false; } else { return true; } } /** * PrepareStatements creates the statements from the SQL expressions so that * they can be run as needed. * * @throws OpenRate.exception.InitializationException */ @Override protected void prepareStatements() throws InitializationException { // prepare our statements try { // prepare the SQL for the TestStatement StmtRUMMapDataSelectQuery = JDBCcon.prepareStatement(RUMMapDataSelectQuery, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); } catch (SQLException ex) { message = "Error preparing the statement " + RUMMapDataSelectQuery; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } catch (Exception ex) { message = "Error preparing the statement <" + RUMMapDataSelectQuery + ">. message: " + ex.getMessage(); OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } // prepare our statements try { // prepare the SQL for the TestStatement StmtPriceModelDataSelectQuery = JDBCcon.prepareStatement(PriceModelDataSelectQuery, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); } catch (SQLException ex) { message = "Error preparing the statement " + PriceModelDataSelectQuery; OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } catch (Exception ex) { message = "Error preparing the statement <" + PriceModelDataSelectQuery + ">. message: " + ex.getMessage(); OpenRate.getOpenRateFrameworkLog().error(message); throw new InitializationException(message, getSymbolicName()); } } /** * Simulate insert at (which is not available in ArrayList * * @param oldList * @param audSeg * @param i * @return */ private ArrayList<RateMapEntry> insertElementAt(ArrayList<RateMapEntry> oldList, RateMapEntry audSeg, int i) { ArrayList<RateMapEntry> newList = new ArrayList<>(); Iterator<RateMapEntry> oldListIter = oldList.iterator(); int position = 0; while (oldListIter.hasNext()) { if (position == i) { newList.add(audSeg); } // add the element from the old list newList.add(oldListIter.next()); position++; } return newList; } }