/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package com.auctionexample;
import java.util.ArrayList;
import java.util.Date;
import java.util.Random;
import org.voltdb.VoltTable;
import org.voltdb.VoltTableRow;
import org.voltdb.VoltType;
import org.voltdb.client.ClientConfig;
import org.voltdb.client.ClientFactory;
/**
* The Client class is the main driver for the Auction example.
* See readme.txt for a description of it's behavior.
*
*/
public class Client {
// Possible results from the BidOnAuction stored procedure
// BidOnAuction procedure maintains an identical list
static final long SUCCESSFULL_BID = 0;
static final long POST_CLOSE_BID = 1;
static final long USER_IS_SELLER = 2;
static final long USER_IS_REBIDDING = 3;
static final long OLD_BID = 4;
static final long LOW_BID = 5;
// never sleep for more than 1/20th of a second
static final int maxSleepMillis = 50;
// bid increase std-dev is one dollar
static final double bidIncreaseFactorStdDev = 1.0;
// print status update every 10 seconds
static final int statusPeriodInSeconds = 10;
// list of all currently running auction ids
static ArrayList<Integer> activeAuctionIds = new ArrayList<Integer>();
// list of all auction ids
static ArrayList<Integer> allAuctionIds;
// list of all user ids
static ArrayList<Integer> userIds;
// random number generator
static Random random = new Random();
// This is hackish, but we insert bids with ids generated on the client side, due
// to a missing "auto-increment" feature. It's actually a tough feature to implement
// given our design, but we think we can add an "auto-unique" without much trouble.
static int nextBidId = -1;
/**
* Get a random new bid amound based on an old bid amount.
*
* @param currentPrice The current bid price in dollars.
* @return A randomly generated value greater than the old given bid price.
*/
static double getRandomNewBidAmount(double currentPrice) {
// determine how much to add to the bid
double increaseFactor = Math.abs(random.nextGaussian()) * bidIncreaseFactorStdDev;
// add to the bid
Double newBid = (currentPrice + increaseFactor) * 100;
// the last three lines just round to the nearest penny (lame)
long money = newBid.longValue();
newBid = (money) / 100.0;
return newBid.doubleValue();
}
/**
* Attempt to make a single bid on an auction.
*
* @param auctionId The id of the auction to bid on.
* @param userId The id of the user making the bid.
* @return A status code which can be found at the top of this class.
*/
static int doBid(org.voltdb.client.Client client, int auctionId, int userId) {
///////////////////////////////////////
// get the current bid price of an item
///////////////////////////////////////
VoltTable infoResult = null;
try {
VoltTable[] infoResultSet = client.callProcedure("GetAuctionInfo", auctionId).getResults();
if (infoResultSet.length != 1) throw new Exception("GetAuctionInfo returned no results");
infoResult = infoResultSet[0];
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
double oldBidAmount = infoResult.fetchRow(0).getDouble("BIDPRICE");
////////////////////////////////////////
// make a new bid on the exact same item
////////////////////////////////////////
double newBidAmount = getRandomNewBidAmount(oldBidAmount);
VoltTable bidResult = null;
try {
VoltTable[] bidResultSet = client.callProcedure("BidOnAuction", auctionId, userId, newBidAmount, nextBidId++).getResults();
if (bidResultSet.length != 1) throw new Exception("BidOnAuction returned no results");
bidResult = bidResultSet[0];
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
// return the result of the bid procedure
// we're primarily interested in POST_CLOSE_BID
return (int)bidResult.asScalarLong();
}
/**
* While there are still active auctions, repeatedly choose a random
* user and an random auction and make a bid.
*/
static void runBidLoop(org.voltdb.client.Client client) {
while (activeAuctionIds.size() > 0) {
// sleep for a random amount of time between each bid
try { Thread.sleep(random.nextInt(maxSleepMillis)); } catch (Exception e) {}
// get a random value from the active auctions and users lists
int itemId = activeAuctionIds.get(random.nextInt(activeAuctionIds.size()));
int userId = userIds.get(random.nextInt(userIds.size()));
// make a bid
long status = doBid(client, itemId, userId);
// end auction if required (remove it from active list)
if (status == POST_CLOSE_BID)
for (int i = 0; i < activeAuctionIds.size(); i++)
if (activeAuctionIds.get(i) == itemId)
activeAuctionIds.remove(i);
}
}
/**
* Loop until all auctions have closed, printing status for each auction at
* specific intervals.
*/
static void runStatusLoop() {
// create a second client handle for this second thread
org.voltdb.client.Client client = null;
try {
ClientConfig config = new ClientConfig("program", "pass");
client = ClientFactory.createClient(config);
client.createConnection("localhost");
} catch (java.io.IOException e) {
e.printStackTrace();
System.exit(-1);
}
boolean auctionsOver = false;
while (auctionsOver == false) {
try {
// assume auctions have ended. if any open auctions are found, then set this false
auctionsOver = true;
// print out the status header
System.out.printf("\nAUCTION STATUS AS OF: %tT\n", new Date());
System.out.printf("+-----------------------------------------------------" +
"-----------------------------------------------+\n");
System.out.printf("| %-20s | %-16s | %-16s | %5s | %8s | %9s | %-6s |\n",
"ITEM", "BIDDER", "SELLER", "BIDS", "PRICE", "END", "STATUS");
System.out.printf("+-----------------------------------------------------" +
"-----------------------------------------------+\n");
// loop over all auction ids, printing a row of status for each one
for (int auctionId : allAuctionIds) {
VoltTable[] statusResultSet = client.callProcedure("AuctionStatus", auctionId).getResults();
if (statusResultSet.length != 1) throw new Exception("AuctionStatus returned no results");
VoltTable statusTable = statusResultSet[0];
VoltTableRow row = statusTable.fetchRow(0);
System.out.printf("| %-20s | %-16s | %-16s | %5d | %8.2f | %9tT | %-6s |\n",
row.getString("item"), row.getString("bidder"), row.getString("seller"),
row.getLong("bidcount"), row.getDouble("price"),
row.getTimestampAsTimestamp("endtime").asApproximateJavaDate(),
row.getString("status"));
if (row.getString("status").equals("OPEN"))
auctionsOver = false;
}
// print the status footer
System.out.printf("+-----------------------------------------------------" +
"-----------------------------------------------+\n");
// wait for next status update
Thread.sleep(1000 * statusPeriodInSeconds);
} catch (Exception e) {
e.printStackTrace();
}
}
try {
client.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* Load the initial data, then run the simulation.
*
* @param args This program uses no command line arguments.
*/
public static void main(String[] args) {
System.out.println("***************************************");
System.out.println("* Welcome to Bobbi's Awesome Auctions *");
System.out.println("* *");
System.out.println("* Connecting to Server... *");
// connect to VoltDB server
org.voltdb.client.Client client = null;
ClientConfig config = new ClientConfig("program", "pass");
client = ClientFactory.createClient(config);
int sleep = 1000;
while(true)
{
try
{
client.createConnection("localhost");
break;
} catch (Exception e) {
System.out.println("Connection failed - retrying in " + (sleep/1000) + " second(s).");
try {Thread.sleep(sleep);} catch(Exception tie){}
if (sleep < 8000)
sleep += sleep;
}
}
System.out.println("* Connected *");
System.out.println("* Running loader *");
System.out.println("***************************************\n");
// get itemId and userId from database
try {
VoltTable modCount = client.callProcedure("@AdHoc", "SELECT ITEMID FROM ITEM").getResults()[0];
allAuctionIds = new ArrayList<Integer>();
userIds = new ArrayList<Integer>();
while( modCount.advanceRow() ){
Integer i = (Integer) modCount.get(0, VoltType.INTEGER);
allAuctionIds.add(i);
}
modCount = client.callProcedure("@AdHoc", "SELECT USERID FROM USER").getResults()[0];
while( modCount.advanceRow() ){
Integer i = (Integer) modCount.get(0, VoltType.INTEGER);
userIds.add(i);
}
activeAuctionIds.addAll(allAuctionIds);
} catch (Exception e) {
e.printStackTrace();
}
// since we create 1 bid per auction
nextBidId = allAuctionIds.size() + 1;
System.out.println("***************************************");
System.out.println("* Finished running loader *");
System.out.println("* Running auctions *");
System.out.println("***************************************");
// create and start a thread that prints auction status every
// 10 seconds, ending when all auctions have closed
Thread statusThread = new Thread(new Runnable() {
public void run() { runStatusLoop(); }
});
statusThread.start();
// runloop executes bids until all auctions have ended
runBidLoop(client);
// wait for the status-printing thread to finish
try { statusThread.join(); } catch (Exception e) {}
// print out a joke with dramatic pauses
System.out.println("\n***************************************");
System.out.println("* Complete... *");
System.out.println("* *");
try { Thread.sleep(1000); } catch (InterruptedException e) {}
System.out.println("* Where do ghosts shop? *");
System.out.println("* *");
try { Thread.sleep(3000); } catch (InterruptedException e) {}
System.out.println("* In Boo-tiques! *");
System.out.println("***************************************");
try {
client.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}