CMSC 433 - Project 1: School Fundraiser Auction House

Due by 23:59:59 on Friday, March 1st.


Goals

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.



System Clients

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.



Bidding Rules

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.



Project structure

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 getItems()
- 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.



Understanding the Buyers and Sellers and Testing

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.



Verification

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!



Java Rules

For this project, you are limited to using synchronized as your guarding mechanism. You are not allowed to use other types of locking mechanism. You are not allowed to use concurrent or self-synchronized collections. This includes a variety of data structures such as the Hashtable. For this reason, before changing any of the provided data structure objects in the skeleton code, make sure they are not concurrent or self-synchronized. Using such data structures will lead to significant deductions during the secret test and hand-grading.

Submit your solution

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!



Grading

  • The "public" test for this project will be a series of single-threaded tests and will be worth 30% of the grade.
  • The "release" tests for this project will be some multi-threaded tests and will be worth 20% of the grade. The submit server will do background re-testing from time to time after your first submission. For this release test, you will get the points if it worked on all of the release tests when you uploaded even if a background retest detects an error. However, for the "secret" tests, if any test fails on any of our attempts, you do not get points for that. So, it will be worth your time to do repeated tests on your own, from the command line, to help you detect whether you're program has errors that only come up in certain scheduling scenarios.
  • The final "secret" tests will be a two-part test. First, your code needs to support concurrent processing, so it can't lock at the method level or lock the bulk of the contents of each method unless the lines all really need to be protected. If it does not meet this requirement, it will not receive any of the 50% of the grade related to this section. If you do support concurrency, your code must also work correctly on the additional multi-threaded tests on which it is run.



    Questions

    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!






    Web Accessibility