/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.aries.samples.ariestrader.util; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Random; /** * TradeConfig is a JavaBean holding all configuration and runtime parameters for the Trade application * TradeConfig sets runtime parameters such as the RunTimeMode (EJB3, DIRECT, SESSION3, JDBC, JPA) * */ public class TradeConfig { /* Trade Runtime Configuration Parameters */ /* Trade Runtime Mode parameters */ public static String[] runTimeModeNames = { "JDBC", "JPA App Managed", "JPA Container Managed"}; public static enum ModeType{JDBC,JPA_AM,JPA_CM} public static ModeType runTimeMode = ModeType.JDBC; /* Trade JPA Layer parameters */ public static String[] jpaLayerNames = {"OpenJPA", "Hibernate"}; public static final int OPENJPA = 0; public static final int HIBERNATE = 1; public static int jpaLayer = OPENJPA; public static String[] orderProcessingModeNames = { "Synchronous", "Asynchronous_2-Phase" }; public static final int SYNCH = 0; public static final int ASYNCH_2PHASE = 1; public static int orderProcessingMode = SYNCH; public static String[] accessModeNames = { "Standard", "WebServices" }; public static final int STANDARD = 0; public static final int WEBSERVICES = 1; private static int accessMode = STANDARD; /* Trade Scenario Workload parameters */ public static String[] workloadMixNames = { "Standard", "High-Volume", }; public final static int SCENARIOMIX_STANDARD = 0; public final static int SCENARIOMIX_HIGHVOLUME = 1; public static int workloadMix = SCENARIOMIX_STANDARD; /* Trade Web Interface parameters */ public static String[] webInterfaceNames = { "JSP", "JSP-Images" }; public static final int JSP = 0; public static final int JSP_Images = 1; public static int webInterface = JSP; /* Trade Caching Type parameters */ public static String[] cachingTypeNames = { "DistributedMap", "Command Caching", "No Caching" }; public static final int DISTRIBUTEDMAP = 0; public static final int COMMAND_CACHING = 1; public static final int NO_CACHING = 2; public static int cachingType = NO_CACHING; /* Trade Database Scaling parameters*/ private static int MAX_USERS = 200; private static int MAX_QUOTES = 400; /* Trade Database specific parameters */ public static String JDBC_UID = null; public static String JDBC_PWD = null; /* OSGi specific parameters */ public static String OSGI_SERVICE_PREFIX = "osgi:service/"; public static String OSGI_DS_NAME_FILTER = "(osgi.jndi.service.name=jdbc/NoTxTradeDataSource)"; /*Trade SOAP specific parameters */ private static String SoapURL = "http://localhost:8080/ariestrader/services/TradeWSServices"; /*Trade XA Datasource specific parameters */ public static boolean JDBCDriverNeedsGlobalTransaction = false; /* Trade Config Miscellaneous items */ public static int KEYBLOCKSIZE = 1000; public static int QUOTES_PER_PAGE = 10; public static boolean RND_USER = true; private static int MAX_HOLDINGS = 10; private static int count = 0; private static Object userID_count_semaphore = new Object(); private static int userID_count = 0; private static String hostName = null; private static Random r0 = new Random(System.currentTimeMillis()); private static Random randomNumberGenerator = r0; public static final String newUserPrefix = "ru:"; public static final int verifyPercent = 5; private static boolean trace = false; private static boolean actionTrace = false; private static boolean updateQuotePrices = true; private static int primIterations = 1; private static boolean longRun = true; private static boolean publishQuotePriceChange = false; /** * -1 means every operation * 0 means never perform a market summary * > 0 means number of seconds between summaries. These will be * synchronized so only one transaction in this period will create a summary and * will cache its results. */ private static int marketSummaryInterval = 20; /* * Penny stocks is a problem where the random price change factor gets a stock * down to $.01. In this case trade jumpstarts the price back to $6.00 to * keep the math interesting. */ public static BigDecimal PENNY_STOCK_PRICE; public static BigDecimal PENNY_STOCK_RECOVERY_MIRACLE_MULTIPLIER; static { PENNY_STOCK_PRICE = new BigDecimal(0.01); PENNY_STOCK_PRICE = PENNY_STOCK_PRICE.setScale(2, BigDecimal.ROUND_HALF_UP); PENNY_STOCK_RECOVERY_MIRACLE_MULTIPLIER = new BigDecimal(600.0); PENNY_STOCK_RECOVERY_MIRACLE_MULTIPLIER.setScale( 2, BigDecimal.ROUND_HALF_UP); } /* CJB (DAYTRADER-25) - Also need to impose a ceiling on the quote price to ensure * prevent account and holding balances from exceeding the databases decimal precision. * At some point, this maximum value can be used to trigger a stock split. */ public static BigDecimal MAXIMUM_STOCK_PRICE; public static BigDecimal MAXIMUM_STOCK_SPLIT_MULTIPLIER; static { MAXIMUM_STOCK_PRICE = new BigDecimal(400); MAXIMUM_STOCK_PRICE.setScale(2, BigDecimal.ROUND_HALF_UP); MAXIMUM_STOCK_SPLIT_MULTIPLIER = new BigDecimal(0.5); MAXIMUM_STOCK_SPLIT_MULTIPLIER.setScale(2, BigDecimal.ROUND_HALF_UP); } /* Trade Scenario actions mixes. Each of the array rows represents a specific Trade Scenario Mix. The columns give the percentages for each action in the column header. Note: "login" is always 0. logout represents both login and logout (because each logout operation will cause a new login when the user context attempts the next action. */ /* Trade Scenario Workload parameters */ public final static int HOME_OP = 0; public final static int QUOTE_OP = 1; public final static int LOGIN_OP = 2; public final static int LOGOUT_OP = 3; public final static int REGISTER_OP = 4; public final static int ACCOUNT_OP = 5; public final static int PORTFOLIO_OP = 6; public final static int BUY_OP = 7; public final static int SELL_OP = 8; public final static int UPDATEACCOUNT_OP = 9; private static int scenarioMixes[][] = { // h q l o r a p b s u { 20, 40, 0, 4, 2, 10, 12, 4, 4, 4 }, //STANDARD { 20, 40, 0, 4, 2, 7, 7, 7, 7, 6 }, //High Volume }; private static char actions[] = { 'h', 'q', 'l', 'o', 'r', 'a', 'p', 'b', 's', 'u' }; private static int sellDeficit = 0; //Tracks the number of buys over sell when a users portfolio is empty // Used to maintain the correct ratio of buys/sells /* JSP pages for all Trade Actions */ public final static int WELCOME_PAGE = 0; public final static int REGISTER_PAGE = 1; public final static int PORTFOLIO_PAGE = 2; public final static int QUOTE_PAGE = 3; public final static int HOME_PAGE = 4; public final static int ACCOUNT_PAGE = 5; public final static int ORDER_PAGE = 6; public final static int CONFIG_PAGE = 7; public final static int STATS_PAGE = 8; //FUTURE Add XML/XSL View public static String webUI[][] = { { "/welcome.jsp", "/register.jsp", "/portfolio.jsp", "/quote.jsp", "/tradehome.jsp", "/account.jsp", "/order.jsp", "/config.jsp", "/runStats.jsp" }, //JSP Interface { "/welcomeImg.jsp", "/registerImg.jsp", "/portfolioImg.jsp", "/quoteImg.jsp", "/tradehomeImg.jsp", "/accountImg.jsp", "/orderImg.jsp", "/config.jsp", "/runStats.jsp" }, //JSP Interface }; /** * Return the hostname for this system * Creation date: (2/16/2000 9:02:25 PM) */ private static String getHostname() { try { if (hostName == null) { hostName = java.net.InetAddress.getLocalHost().getHostName(); //Strip of fully qualified domain if necessary try { hostName = hostName.substring(0, hostName.indexOf('.')); } catch (Exception e) { } } } catch (Exception e) { Log.error( "Exception getting local host name using 'localhost' - ", e); hostName = "localhost"; } return hostName; } /** * Return a Trade UI Web page based on the current configuration * This may return a JSP page or a Servlet page * Creation date: (3/14/2000 9:08:34 PM) */ public static String getPage(int pageNumber) { return webUI[webInterface][pageNumber]; } /** * Return the list of run time mode names * Creation date: (3/8/2000 5:58:34 PM) * @return java.lang.String[] */ public static java.lang.String[] getRunTimeModeNames() { return runTimeModeNames; } private static int scenarioCount = 0; /** * Return a Trade Scenario Operation based on the setting of the current mix (TradeScenarioMix) * Creation date: (2/10/2000 9:08:34 PM) */ public static char getScenarioAction(boolean newUser) { int r = rndInt(100); //0 to 99 = 100 int i = 0; int sum = scenarioMixes[workloadMix][i]; while (sum <= r) { i++; sum += scenarioMixes[workloadMix][i]; } incrementScenarioCount(); /* In TradeScenarioServlet, if a sell action is selected, but the users portfolio is empty, * a buy is executed instead and sellDefecit is incremented. This allows the number of buy/sell * operations to stay in sync w/ the given Trade mix. */ if ((!newUser) && (actions[i] == 'b')) { synchronized (TradeConfig.class) { if (sellDeficit > 0) { sellDeficit--; return 's'; //Special case for TradeScenarioServlet to note this is a buy switched to a sell to fix sellDeficit } } } return actions[i]; } public static String getUserID() { String userID; if (RND_USER) { userID = rndUserID(); } else { userID = nextUserID(); } return userID; } private static final BigDecimal orderFee = new BigDecimal("24.95"); private static final BigDecimal cashFee = new BigDecimal("0.0"); public static BigDecimal getOrderFee(String orderType) { if ((orderType.compareToIgnoreCase("BUY") == 0) || (orderType.compareToIgnoreCase("SELL") == 0)) return orderFee; return cashFee; } /** * Increment the sell deficit counter * Creation date: (6/21/2000 11:33:45 AM) */ public synchronized static void incrementSellDeficit() { sellDeficit++; } public static String nextUserID() { String userID; synchronized (userID_count_semaphore) { userID = "uid:" + userID_count; userID_count++; if (userID_count % MAX_USERS == 0) { userID_count = 0; } } return userID; } public static double random() { return randomNumberGenerator.nextDouble(); } public static String rndAddress() { return rndInt(1000) + " Oak St."; } public static String rndBalance() { //Give all new users a cool mill in which to trade return "1000000"; } public static String rndCreditCard() { return rndInt(100) + "-" + rndInt(1000) + "-" + rndInt(1000) + "-" + rndInt(1000); } public static String rndEmail(String userID) { return userID + "@" + rndInt(100) + ".com"; } public static String rndFullName() { return "first:" + rndInt(1000) + " last:" + rndInt(5000); } public static int rndInt(int i) { return (new Float(random() * i)).intValue(); } public static float rndFloat(int i) { return (new Float(random() * i)).floatValue(); } public static BigDecimal rndBigDecimal(float f) { return (new BigDecimal(random() * f)).setScale( 2, BigDecimal.ROUND_HALF_UP); } public static boolean rndBoolean() { return randomNumberGenerator.nextBoolean(); } /** * Returns a new Trade user * Creation date: (2/16/2000 8:50:35 PM) */ public synchronized static String rndNewUserID() { return newUserPrefix + getHostname() + System.currentTimeMillis() + count++; } public static float rndPrice() { return ((new Integer(rndInt(200))).floatValue()) + 1.0f; } private final static BigDecimal ONE = new BigDecimal(1.0); public static BigDecimal getRandomPriceChangeFactor() { double percentGain = rndFloat(1) * 0.2; if (random() < .5) percentGain *= -1; percentGain += 1; // change factor is between +/- 20% BigDecimal percentGainBD = (new BigDecimal(percentGain)).setScale(2, BigDecimal.ROUND_HALF_UP); if (percentGainBD.doubleValue() <= 0.0) percentGainBD = ONE; return percentGainBD; } public static float rndQuantity() { return ((new Integer(rndInt(200))).floatValue()) + 1.0f; } public static String rndSymbol() { return "s:" + rndInt(MAX_QUOTES - 1); } public static String rndSymbols() { String symbols = ""; int num_symbols = rndInt(QUOTES_PER_PAGE); for (int i = 0; i <= num_symbols; i++) { symbols += "s:" + rndInt(MAX_QUOTES - 1); if (i < num_symbols) symbols += ","; } return symbols; } public static String rndUserID() { String nextUser = getNextUserIDFromDeck(); if (Log.doTrace()) Log.trace("TradeConfig:rndUserID -- new trader = " + nextUser); return nextUser; } private static synchronized String getNextUserIDFromDeck() { int numUsers = getMAX_USERS(); if (deck == null) { deck = new ArrayList(numUsers); for (int i = 0; i < numUsers; i++) deck.add(i, new Integer(i)); java.util.Collections.shuffle(deck, r0); } if (card >= numUsers) card = 0; return "uid:" + deck.get(card++); } //Trade implements a card deck approach to selecting // users for trading with tradescenarioservlet private static ArrayList deck = null; private static int card = 0; /** * Set the list of run time mode names * Creation date: (3/8/2000 5:58:34 PM) * @param newRunTimeModeNames java.lang.String[] */ public static void setRunTimeModeNames( java.lang.String[] newRunTimeModeNames) { runTimeModeNames = newRunTimeModeNames; } /** * This is a convenience method for servlets to set Trade configuration parameters * from servlet initialization parameters. The servlet provides the init param and its * value as strings. This method then parses the parameter, converts the value to the * correct type and sets the corresponding TradeConfig parameter to the converted value * */ public static void setConfigParam(String parm, String value) { Log.log("TradeConfig setting parameter: " + parm + "=" + value); // Compare the parm value to valid TradeConfig parameters that can be set // by servlet initialization // First check the proposed new parm and value - if empty or null ignore it if (parm == null) return; parm = parm.trim(); if (parm.length() <= 0) return; if (value == null) return; value = value.trim(); if (parm.equalsIgnoreCase("runTimeMode")) { try { for (int i = 0; i < runTimeModeNames.length; i++) { if (value.equalsIgnoreCase(runTimeModeNames[i])) { setRunTimeMode(ModeType.values()[i]); break; } } } catch (Exception e) { Log.error( "TradeConfig.setConfigParm(..): minor exception caught" + "trying to set runtimemode to " + value + "reverting to current value: " + runTimeModeNames[getRunTimeMode().ordinal()], e); } // If the value is bad, simply revert to current } else if (parm.equalsIgnoreCase("orderProcessingMode")) { try { for (int i = 0; i < orderProcessingModeNames.length; i++) { if (value.equalsIgnoreCase(orderProcessingModeNames[i])) { orderProcessingMode = i; break; } } } catch (Exception e) { Log.error( "TradeConfig.setConfigParm(..): minor exception caught" + "trying to set orderProcessingMode to " + value + "reverting to current value: " + orderProcessingModeNames[orderProcessingMode], e); } // If the value is bad, simply revert to current } else if (parm.equalsIgnoreCase("accessMode")) { try { for (int i = 0; i < accessModeNames.length; i++) { if (value.equalsIgnoreCase(accessModeNames[i])) { accessMode = i; break; } } } catch (Exception e) { Log.error( "TradeConfig.setConfigParm(..): minor exception caught" + "trying to set accessMode to " + value + "reverting to current value: " + accessModeNames[accessMode], e); } } else if (parm.equalsIgnoreCase("webServicesEndpoint")) { try { setSoapURL(value); } catch (Exception e) { Log.error( "TradeConfig.setConfigParm(..): minor exception caught" + "Setting web services endpoint", e); } //On error, revert to saved } else if (parm.equalsIgnoreCase("workloadMix")) { try { for (int i = 0; i < workloadMixNames.length; i++) { if (value.equalsIgnoreCase(workloadMixNames[i])) { workloadMix = i; break; } } } catch (Exception e) { Log.error( "TradeConfig.setConfigParm(..): minor exception caught" + "trying to set workloadMix to " + value + "reverting to current value: " + workloadMixNames[workloadMix], e); } // If the value is bad, simply revert to current } else if (parm.equalsIgnoreCase("WebInterface")) { try { for (int i = 0; i < webInterfaceNames.length; i++) { if (value.equalsIgnoreCase(webInterfaceNames[i])) { webInterface = i; break; } } } catch (Exception e) { Log.error( "TradeConfig.setConfigParm(..): minor exception caught" + "trying to set WebInterface to " + value + "reverting to current value: " + webInterfaceNames[webInterface], e); } // If the value is bad, simply revert to current } else if (parm.equalsIgnoreCase("CachingType")) { try { for (int i = 0; i < cachingTypeNames.length; i++) { if (value.equalsIgnoreCase(cachingTypeNames[i])) { cachingType = i; break; } } } catch (Exception e) { Log.error( "TradeConfig.setConfigParm(..): minor exception caught" + "trying to set CachingType to " + value + "reverting to current value: " + cachingTypeNames[cachingType], e); } // If the value is bad, simply revert to current } else if (parm.equalsIgnoreCase("maxUsers")) { try { MAX_USERS = Integer.parseInt(value); } catch (Exception e) { Log.error( "TradeConfig.setConfigParm(..): minor exception caught" + "Setting maxusers, error parsing string to int:" + value + "revering to current value: " + MAX_USERS, e); } //On error, revert to saved } else if (parm.equalsIgnoreCase("maxQuotes")) { try { MAX_QUOTES = Integer.parseInt(value); } catch (Exception e) { Log.error( "TradeConfig.setConfigParm(...) minor exception caught" + "Setting max_quotes, error parsing string to int " + value + "reverting to current value: " + MAX_QUOTES, e); } //On error, revert to saved } else if (parm.equalsIgnoreCase("primIterations")) { try { primIterations = Integer.parseInt(value); } catch (Exception e) { Log.error( "TradeConfig.setConfigParm(..): minor exception caught" + "Setting primIterations, error parsing string to int:" + value + "revering to current value: " + primIterations, e); } //On error, revert to saved } } /** * Gets the orderProcessingModeNames * @return Returns a String[] */ public static String[] getOrderProcessingModeNames() { return orderProcessingModeNames; } /** * Gets the workloadMixNames * @return Returns a String[] */ public static String[] getWorkloadMixNames() { return workloadMixNames; } /** * Gets the webInterfaceNames * @return Returns a String[] */ public static String[] getWebInterfaceNames() { return webInterfaceNames; } /** * Gets the webInterfaceNames * @return Returns a String[] */ public static String[] getCachingTypeNames() { return cachingTypeNames; } /** * Gets the scenarioMixes * @return Returns a int[][] */ public static int[][] getScenarioMixes() { return scenarioMixes; } /** * Gets the trace * @return Returns a boolean */ public static boolean getTrace() { return trace; } /** * Sets the trace * @param trace The trace to set */ public static void setTrace(boolean traceValue) { trace = traceValue; } /** * Gets the mAX_USERS. * @return Returns a int */ public static int getMAX_USERS() { return MAX_USERS; } /** * Sets the mAX_USERS. * @param mAX_USERS The mAX_USERS to set */ public static void setMAX_USERS(int mAX_USERS) { MAX_USERS = mAX_USERS; deck = null; // reset the card deck for selecting users } /** * Gets the mAX_QUOTES. * @return Returns a int */ public static int getMAX_QUOTES() { return MAX_QUOTES; } /** * Sets the mAX_QUOTES. * @param mAX_QUOTES The mAX_QUOTES to set */ public static void setMAX_QUOTES(int mAX_QUOTES) { MAX_QUOTES = mAX_QUOTES; } /** * Gets the mAX_HOLDINGS. * @return Returns a int */ public static int getMAX_HOLDINGS() { return MAX_HOLDINGS; } /** * Sets the mAX_HOLDINGS. * @param mAX_HOLDINGS The mAX_HOLDINGS to set */ public static void setMAX_HOLDINGS(int mAX_HOLDINGS) { MAX_HOLDINGS = mAX_HOLDINGS; } /** * Gets the actionTrace. * @return Returns a boolean */ public static boolean getActionTrace() { return actionTrace; } /** * Sets the actionTrace. * @param actionTrace The actionTrace to set */ public static void setActionTrace(boolean actionTrace) { TradeConfig.actionTrace = actionTrace; } /** * Gets the scenarioCount. * @return Returns a int */ public static int getScenarioCount() { return scenarioCount; } /** * Sets the scenarioCount. * @param scenarioCount The scenarioCount to set */ public static void setScenarioCount(int scenarioCount) { TradeConfig.scenarioCount = scenarioCount; } public static synchronized void incrementScenarioCount() { scenarioCount++; } /** * Gets the jdbc driver needs global transaction * Some XA Drivers require a global transaction to be started * for all SQL calls. To work around this, set this to true * to cause the direct mode to start a user transaction. * @return Returns a boolean */ public static boolean getJDBCDriverNeedsGlobalTransaction() { return JDBCDriverNeedsGlobalTransaction; } /** * Sets the jdbc driver needs global transaction * @param JDBCDriverNeedsGlobalTransactionVal the value */ public static void setJDBCDriverNeedsGlobalTransaction(boolean JDBCDriverNeedsGlobalTransactionVal) { JDBCDriverNeedsGlobalTransaction = JDBCDriverNeedsGlobalTransactionVal; } /** * Gets the updateQuotePrices. * @return Returns a boolean */ public static boolean getUpdateQuotePrices() { return updateQuotePrices; } /** * Sets the updateQuotePrices. * @param updateQuotePrices The updateQuotePrices to set */ public static void setUpdateQuotePrices(boolean updateQuotePrices) { TradeConfig.updateQuotePrices = updateQuotePrices; } public static String getSoapURL() { return SoapURL; } public static void setSoapURL(String value) { SoapURL = value; } public static int getAccessMode() { return accessMode; } public static void setAccessMode(int value) { accessMode = value; } public static ModeType getRunTimeMode() { return runTimeMode; } public static void setRunTimeMode(ModeType value) { runTimeMode = value; } public static int getPrimIterations() { return primIterations; } public static void setPrimIterations(int iter) { primIterations = iter; } public static boolean getLongRun() { return longRun; } public static void setLongRun(boolean longRun) { TradeConfig.longRun = longRun; } public static void setPublishQuotePriceChange(boolean publishQuotePriceChange) { TradeConfig.publishQuotePriceChange = publishQuotePriceChange; } public static boolean getPublishQuotePriceChange() { return publishQuotePriceChange; } public static void setMarketSummaryInterval(int seconds) { TradeConfig.marketSummaryInterval = seconds; } public static int getMarketSummaryInterval() { return TradeConfig.marketSummaryInterval; } /** * Return the list of JPA Layer names * Creation date: (01/10/2009) * @return java.lang.String[] */ public static java.lang.String[] getJPALayerNames() { return jpaLayerNames; } }