| /** |
| * 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.openjpa.integration.daytrader; |
| |
| import java.io.IOException; |
| import java.util.Collection; |
| import java.util.Iterator; |
| |
| /** |
| * TradeScenarioServlet emulates a population of web users by generating a specific Trade operation |
| * for a randomly chosen user on each access to the URL. Test this servlet by clicking Trade Scenario |
| * and hit "Reload" on your browser to step through a Trade Scenario. To benchmark using this URL aim |
| * your favorite web load generator (such as AKStress) at the Trade Scenario URL and fire away. |
| */ |
| public class TradeScenario { |
| |
| private TradeAction tAction = null; |
| |
| public TradeScenario(TradeAction tAction) { |
| this.tAction = tAction; |
| } |
| |
| /** |
| * Perform the following 15 tasks for the given userID: |
| * login, home, account, update, home, portfolio, sell, buy, home, portfolio, sell, buy, home, account, logout |
| * |
| * @param userID |
| */ |
| public boolean performUserTasks(String userID) { |
| StringBuilder sb = new StringBuilder(16384); |
| boolean brc = false; |
| |
| if (TradeConfig.log.isTraceEnabled()) { |
| TradeConfig.log.trace("TradeScenario.performUserTasks(" + userID + ")"); |
| } |
| |
| try { |
| // login |
| log(sb, performTask("l", userID)); |
| // home page |
| log(sb, performTask("h", userID)); |
| // account info and orders |
| log(sb, performTask("a", userID)); |
| // update account info |
| log(sb, performTask("u", userID)); |
| // home page |
| log(sb, performTask("h", userID)); |
| // portfolio holdings |
| log(sb, performTask("p", userID)); |
| // sell |
| log(sb, performTask("s", userID)); |
| // buy |
| log(sb, performTask("b", userID)); |
| // home page |
| log(sb, performTask("h", userID)); |
| // portfolio holdings |
| log(sb, performTask("p", userID)); |
| // sell |
| log(sb, performTask("s", userID)); |
| // buy |
| log(sb, performTask("b", userID)); |
| // home page |
| log(sb, performTask("h", userID)); |
| // account info and orders |
| log(sb, performTask("a", userID)); |
| // logout |
| log(sb, performTask("o", userID)); |
| brc = true; |
| if (TradeConfig.log.isTraceEnabled()) { |
| TradeConfig.log.trace(sb.toString()); |
| } |
| } catch (Exception e) { |
| TradeConfig.log.error("TradeScenario.performUserTasks(" + userID + ") failed", e); |
| } |
| return brc; |
| } |
| |
| /** |
| * Main service method for TradeScenarioServlet |
| * |
| * @param request Object that encapsulates the request to the servlet |
| * @param response Object that encapsulates the response from the servlet |
| */ |
| public String performTask(String scenarioAction, String userID) throws IOException { |
| StringBuilder sb = new StringBuilder(256); |
| String results = ""; |
| // Scenario generator for Trade2 |
| char action = ' '; |
| |
| // String to create full dispatch path to TradeAppServlet w/ request Parameters |
| String dispPath = null; // Dispatch Path to TradeAppServlet |
| |
| if ((scenarioAction != null) && (scenarioAction.length() >= 1)) |
| { |
| action = scenarioAction.charAt(0); |
| if (action == 'n') |
| { //null; |
| try |
| { |
| log(sb, "TradeScenario.performTask() scenarioAction=" + scenarioAction + ", userID=" + userID); |
| } |
| catch (Exception e) |
| { |
| log(sb, "ERROR - TradeScenario.performTask() - Exception ", e); |
| } finally { |
| return sb.toString(); |
| } |
| } //end of action=='n' |
| } |
| |
| if (userID == null || userID.trim().length() == 0) { |
| // These operations require the user to be logged in. Verify the user and if not logged in |
| // change the operation to a login |
| userID = null; |
| action = 'l'; |
| TradeConfig.incrementScenarioCount(); |
| } else if (action == ' ') { |
| //action is not specified perform a random operation according to current mix |
| // Tell getScenarioAction if we are an original user or a registered user |
| // -- sellDeficits should only be compensated for with original users. |
| action = TradeConfig.getScenarioAction( |
| userID.startsWith(TradeConfig.newUserPrefix)); |
| } |
| |
| switch (action) { |
| case 'q' : //quote |
| tAction.doQuotes(sb, userID, TradeConfig.rndSymbols()); |
| break; |
| case 'a' : //account |
| tAction.doAccount(sb, userID, results); |
| break; |
| case 'u' : //update account profile |
| String fullName = "rnd" + System.currentTimeMillis(); |
| String address = "rndAddress"; |
| String password = "xxx"; |
| String email = "rndEmail"; |
| String creditcard = "rndCC"; |
| tAction.doAccountUpdate(sb, userID, password, password, fullName, address, creditcard, email); |
| break; |
| case 'h' : //home |
| tAction.doHome(sb, userID, results); |
| break; |
| case 'l' : //login |
| userID = TradeConfig.getUserID(); |
| String password2 = "xxx"; |
| boolean brc = tAction.doLogin(sb, userID, password2); |
| // login is successful if the userID is written to the HTTP session |
| if (!brc) { |
| log(sb, "TradeScenario login failed. Reset DB between runs."); |
| } |
| break; |
| case 'o' : //logout |
| tAction.doLogout(sb, userID); |
| break; |
| case 'p' : //portfolio |
| tAction.doPortfolio(sb, userID, results); |
| break; |
| case 'r' : //register |
| //Logout the current user to become a new user |
| // see note in TradeServletAction |
| tAction.doLogout(sb, userID); |
| |
| userID = TradeConfig.rndNewUserID(); |
| String passwd = "yyy"; |
| fullName = TradeConfig.rndFullName(); |
| creditcard = TradeConfig.rndCreditCard(); |
| String money = TradeConfig.rndBalance(); |
| email = TradeConfig.rndEmail(userID); |
| String smail = TradeConfig.rndAddress(); |
| tAction.doRegister(sb, userID, passwd, passwd, fullName, creditcard, money, email, smail); |
| break; |
| case 's' : //sell |
| Collection<HoldingDataBean> holdings = tAction.getHoldings(userID); |
| int numHoldings = holdings.size(); |
| if (numHoldings > 0) |
| { |
| //sell first available security out of holding |
| Iterator it = holdings.iterator(); |
| boolean foundHoldingToSell = false; |
| while (it.hasNext()) |
| { |
| HoldingDataBean holdingData = (HoldingDataBean) it.next(); |
| if ( !(holdingData.getPurchaseDate().equals(new java.util.Date(0))) ) |
| { |
| Integer holdingID = holdingData.getHoldingID(); |
| tAction.doSell(sb, userID, holdingID); |
| foundHoldingToSell = true; |
| break; |
| } |
| } |
| if (foundHoldingToSell) break; |
| TradeJPADirect.log.warn("TradeScenario: No holdings sold for userID=" + userID + |
| ", holdings=" + numHoldings); |
| } else { |
| TradeJPADirect.log.warn("TradeScenario: No holdings to sell for userID=" + userID); |
| } |
| // At this point: A TradeScenario Sell was requested with No Stocks in Portfolio |
| // This can happen when a new registered user happens to request a sell before a buy |
| // In this case, fall through and perform a buy instead |
| TradeJPADirect.log.warn("TradeScenario: No holdings sold - switching to buy -- userID=" + userID); |
| |
| /* Trade 2.037: Added sell_deficit counter to maintain correct buy/sell mix. |
| * When a users portfolio is reduced to 0 holdings, a buy is requested instead of a sell. |
| * This throws off the buy/sell mix by 1. This results in unwanted holding table growth |
| * To fix we increment a sell deficit counter to maintain the correct ratio in getScenarioAction |
| * The 'z' action from getScenario denotes that this is a sell action that was switched from a buy |
| * to reduce a sellDeficit |
| */ |
| if (!userID.startsWith(TradeConfig.newUserPrefix)) |
| { |
| TradeConfig.incrementSellDeficit(); |
| } |
| case 'b' : //buy |
| String symbol = TradeConfig.rndSymbol(); |
| String amount = TradeConfig.rndQuantity() + ""; |
| tAction.doQuotes(sb, userID, symbol); |
| tAction.doBuy(sb, userID, symbol, amount); |
| break; |
| } //end of switch statement |
| log(sb, "Results", results); |
| return sb.toString(); |
| } |
| |
| private void log(StringBuilder sb, String msg) { |
| if (TradeJPADirect.log != null && TradeJPADirect.log.isTraceEnabled()) { |
| sb.append(msg); |
| sb.append(System.getProperty("line.separator")); |
| } |
| } |
| |
| private void log(StringBuilder sb, String msg, Object obj) { |
| if (TradeJPADirect.log != null && TradeJPADirect.log.isTraceEnabled()) { |
| sb.append(msg); |
| sb.append(" = "); |
| sb.append(obj); |
| sb.append(System.getProperty("line.separator")); |
| } |
| } |
| } |