You have been contracted by a high school to build a thread-safe auction house styled bidding server. They don't want you to simply force everyone to line up to be processed one at a time, so you'll need to think about what to lock and when. For example, if you haven't checked whether a bidder has already exceeded their bid limits, there's no reason to start locking the list of items up for bidding. They will play the auction house and do some fund-raising for new music equipment this way. Some of the concepts and techniques you may find relevant and helpful as you are working on this system are atomicity and concurrent access to collections.
Your system will follow a basic client/server model.
The site will be visited by two types of clients: Sellers and Buyers.
A Seller has one ability - they can submit an item to be listed on the server.
A Buyer has more abilities - they can do any of the following:
- request a list of the items currently listed
- check the price of an individual item
- place a bid on an individual item
- checks whether a bid they places was successful
All of the above activities will be supported by the methods you will
implement for the auction server.
More details on these methods can be found later.
The high school has provided you with a set of rules to try to make selling and the bidding process
fair and orderly.
The ServerDataAndProcessing class that you are implementing for this project will need to enforce these rules.
Listing:
- each seller will be limited to a fixed number of different items they can have listed
at any given time. This number will be identified as maxSellerItems.
- while there will be no minimum number of items, the site will have fixed maximum number
of items that can be open for bidding at any given time. This number will be identified
as serverCapacity.
- all prices will be quoted in integer dollars-only
- an item must be listed with an opening bidding price of at least $1.
- it will attempt to exclude "luxury" items by requiring sellers to set the opening biding
price at less than $100.
- when an item is listed, a unique listing ID is generated and assigned to the item in such
a way that no two items ever listed are allowed to share the same listing ID.
Bidding:
- the bidding price for an item must be at least as much as the opening bidding price
set by the seller of the item.
- a buyer can ask the server for the current bid price of an item.
- a buyer will only be allowed to hold active bids on a maximum number of different items
and this limit will be identified as maxBidCount.
- once a buyer has placed a bid on an item, they will only be allowed to successfully place
another bid if another bidder has placed a higher bid than their maximum bid had been (so
if a buyer is already the top bidder on an item and tries to place a new bid on the same
item, the second bid will be ignored).
- an item can receive as many bids as are validly offered as long as the item listing
has not expired.
- once a buyer has placed a bid, that buyer is committed to buying the item if they end up
being the winning bidder (no back-outs).
- if the bidding period for an item expires before anybody has placed a bid on it, the item
is just discarded - the seller won't put it up for sale again and the server will not claim
any gains for it.
For the purpose of this project, you will implement the server as a
mutable singleton object
that will be shared by MANY client threads.
For simplicity (and staying on the goals of this project)
the server and client threads will all be running in the same process.
You will NOT be implementing a real Web-based server/client application.
The only class that you are going to modify is ServerDataAndProcessing.java (the others have been provided
and should be used as they are - you can look at them but not change them).
Methods
int submitItem(String sellerName, String itemName, int lowestBiddingPrice, int biddingDurationMs)
- A seller client may call this method to submit an item to be listed by the bidding server.
A seller client uses sellerName and itemName to identify itself and the item that is submitted.
The unit of BiddingDuration is in milliseconds.
If the item can be successfully placed, this method returns the unique listing ID generated by the bidding server.
If the item cannot be placed, for instance, the seller has already used up its quota or the site's capacity has
been reached (already maxSellerItems items listed), this method returns -1.
List
- A buyer client may call this method to retrieve a copy of the list of items currently
listed as active.
Each Item object in the list provides access to the name of the item and
the initial minimum bidding price (note: the current bid price of the item may
have changed from its initial value and the actual bid price can be retrieved by
calling the method itemPrice() which is described next.
int itemPrice(int listingID)
- A buyer can check the current bid/opening price for an item by supplying the unique listing
ID of that item. The value returned by this method needs to be the highest bid made so
far, or if nobody made a bid on the item the minimum bid value supplied by the seller
when he submitted the item.
If there is no item with the supplied listing ID the method indicates an error by
returning a value of -1.
Boolean itemUnbid(int listingID)
- A buyer can check whether an item has not yet been bid upon by supplying the unique listing
ID of that item. The value returned by this method is based on whether any bid has been successfully
placed on the item yet.
If there is no item with the supplied listing ID the method returns a value of true since it is
true that the non-existing item has not yet been successfully bid upon.
boolean submitBid(String buyerName, int listingID, int biddingAmount)
- A buyer client calls this method to submit a bid for a listed item. This method returns true if the bid is successfully
submitted and false if the submission request is rejected. There are several situations when a bid submission request can be
rejected. If a bidder already has bid on too many items, the bidder is not allowed to place bids on new items. If a bidder
already has a bid on an item, the bidder is not allowed to place a new bid on the same item until another buyer has placed a
higher bid. The bid can also be rejected if the item is no longer for sale, or if the listing ID corresponds to none of the
items submitted by the sellers. You can assume for this project that a
single buyer will not be trying to place more than one bid at the same
moment.
int checkBidStatus(String buyerName, int listingID)
- A buyer client calls this method to poll the bidding server to check the status of a bid the buyer may have on an item.
There are three possible status results;
(1) SUCCESS [return value 1]: if this item's bidding duration has passed and the buyer has the highest bid,
(2) OPEN [return value 2]: if this item is still receiving bids, and
(3) FAILED [return value 3]: If bidding is over and this buyer did not win, or if the listing ID doesn't
correspond to any item submitted by the sellers.
As part of its job, if the item being checked is no longer open and still appears on the list of items currently
listed as active, this method will remove it from that list and update the appropriate locations to reflect that
it is no longer being bid upon and also update the appropriate fields if it was successfully sold to anyone.
Fields
The limits mentioned earlier in the description are all stored
as public constant integers. Please note that we can change these
values as part of our grading but we will not change the names
of the constants. They will be:
- an integer constant called maxBidCount
- an integer constant called maxSellerItems
- an integer constant called serverCapacity
The high school has requested to have easy and frequent access to two
statistics; the total number of items that have been sold up to that
point, and the total amount they've raised for themselves. To make
this easy and to avoid delay when these values are requested, you need
to maintain two mutable fields that keep track of these stats as things
are sold.
- a mutable integer called soldItemsCount
(the total number of items that have been sold so far)
- a mutable integer called revenue
(the revenue generated so far)
Of course, since this project is running a multi-threaded system one of your tasks
will be to make sure that concurrent requests to modify to these fields are handled
safely.
As was mentioned above, the Buyer and Seller client classes have been
implemented already.
You'll note that they both implement the Client interface that is also provided.
We have also provided an Item class.
Of course, we might use different (valid to the spec) code for part of the testing.
We are also providing some example test cases, but you should not rely solely on them
for either debugging or for thorough testing.
A solution that passes all the provided test cases may still lose points at
grading time if the solution is found to have flaws detected by other tests we have.
Please see the grading section below for more detail.
A test program is written to run instances of many client classes in multiple threads,
accessing the shared singleton instance of the auction ServerDataAndProcessing class that you
are implementing in this project.
The lifecycle of the test program follows the following three stages:
1. Create several clients and execute them on multiple threads.
2. Wait for all of the clients to finish.
3. Verify the correctness of the system state based on what we know it should be.
Client Behaviors
The behaviors of the Seller and Buyer clients are described here.
The source code of these clients is provided to allow you to obtain a better
understanding how these types of clients can interact with the auction house
ServerDataAndProcessing that you will implement.
A typical Seller client will behave in the following way:
- it will hold a list of items to sell (ours
generates a large number of items at random to try to sell).
- it is initialized with things such as a unique name and how many
cycles it should execute and the longest it should sleep between
attempts to list an item.
- when the thread is run it enters a loop and iterates the specified
number of cycles, where in each cycle it will
(a) randomly pick an item and tries to submit it to the server, then
(b) if the submission is accepted, that item is then removed from
its own list of things it wants to sell, and then
(c) after each submission attempt to the server, the seller sleeps
for some random amount of time.
A typical Buyer client will behave in the following way:
- it is initialized with things such as a unique name and how much
cash it has available to spend, how many cycles it should TRY to execute
(if it's out of cash, why bother continuing) and the longest it should
sleep between attempts to buy and check items.
- when the thread is run it enters a loop and iterates the specified
number of cycles (though there is an escape clause for if it runs out
of cash) where in each cycle it will
(a) try to buy things and (b) check up on all of its active bids.
- To try to buy things within each cycle, it will
(a) retrieve a list of items available for sale, then
(b) randomly picks an item that it can afford going on the worst-case
assumption that it wins all outstanding bids, and
(c) adds $1 to the highest bidding price and makes the bid
- To check up on all of its active bids within each cycle, it will
(a) check the status of the bid and
(b) if the bid was successful (ie: it won the auction) it will deduct
the price of that item from its cash reserve.
Note that these clients are not necessarily designed to follow common sense or
even follow the High School's rules for things like the limit on how many items
a seller can try to sell.
It is the responsibility of the ServerDataAndProcessing class to enforce the rules and report failures
when rules are violated.
Correctness of your ServerDataAndProcessing can be verified by test programs in several ways. Here are two major examples. First, at the end of execution of all actions it can check whether the things like the total number of items sold or the revenue as seen by the individual buyers and sellers actually matches the values for those maintained internally by the server in the statistics variables. The total revenue generated overall can be calculated from the buyers' perspective by iterating through all of the buyer objects and taking the sum of the prices they each paid for the items they won. The total revenue as seen by the whole of the buyers and sellers and by the server itself would be the same. Second, the test program can verify whether all of the bidding rules have been strictly enforced by generating specific bids where it knows what the outcomes should be and then confirm that those were the actual outcomes. In other words, the invariants associated with the bidding rules must hold true at all points. For example, the number of items currently listed at any point should not exceed ServerDataAndProcessing.serverCapacity or something went wrong. If the single-threaded tests (eg: ServerTestSingleThread) don't pass, or if your code does not compile, you will not be given any credit for the project. For more than minimal points, the multi-thread test cases should of course be working as well!
You will submit to the submit.cs.umd.edu server. To be sure you don't get severely penalized during the testing phase, don't change any classes except for ServerDataAndProcessing.java, don't add new classes, and don't change the directory structure!
Don't forget the course rules on discussing aspects of the project and ban on exchanging code or looking at each other's code. If in doubt, contact Dr. Golub first!