blob: acafeb4a1a1672310d4a0eabb381a5a3f7890ce4 [file] [log] [blame]
/**
* 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"));
}
}
}