Skip to main content

CSE 331 Homework 7: Ticket Buying App

Due Friday, June 5 at 11:59pm.

In this homework, you will make your queue data structure generic and use it to build a small ticket buying web application with a JavaScript frontend.

Getting started

Get the starter code via gitlab as before (download directory as zip, then unpack it).

Most of the files are similar to HW6. The differences are:

  • genericqueue/: a new package replacing officehours/. The interface (GenericQueue.java) is a generic version of the HW5/HW6 queue, parameterized by a value type V, with renamed methods and tightened time/space bounds.
  • tickets/TicketApp.java: a new starter file for your web app. Unlike the HW6 web app, it serves a JSON API rather than rendering HTML pages.
  • static/: a new directory containing the HTML, JavaScript, and CSS files that the browser loads. Replaces the HW6 templates/ directory. (There are no JTE templates in this assignment).

After unpacking the HW7 starter code, you should copy the contents of your HW6 OfficeHoursQueueImpl.java into GenericQueueImpl.java. Be careful when copying, because HW7 uses a different package name. The starter code has the correct package declaration (package genericqueue;), so don't overwrite it when pasting in your HW6 code. Then rename your class to GenericQueueImpl to agree with the new filename.

Your copied code will not compile immediately because the interface has been renamed and generic-ified. You will need to make the changes described in Task 1 below.

If you are using VS Code, be sure to open the hw07 folder as the "root" folder in your editor, or else it will not be able to find the dependencies in lib/.

Task 1: Implement GenericQueue

GenericQueue is a generic version of the OfficeHoursQueue from HW5/HW6.

One way to think about the HW5/HW6 queue data structure is as a kind of "key-value queue", that is, a queue whose entries can be referred to by a unique String key (the student's name). And the queue also stores for each name, an associated String "value" (the student's question).

GenericQueue is similar, except that the type of the associated value is a type parameter V of the interface. Note that the "keys" are still Strings and not some other generic type. We continue to refer to the keys as "names".

A GenericQueue<String> stores string values (like questions), which essentially reproduces the behavior of this data structure on HW5/HW6. But the value can also take on other types. For example, a GenericQueue<Integer> stores a queue whose values are integers (but again, the keys/names are still strings).

The methods of GenericQueue are mostly analogous to those in HW6. A few methods have been updated to reflect the generalized value type:

  • getQuestion(name) is now getEntry(name), and it returns an Entry<V> (and not a V!)
  • updateQuestion(name, question) is now updateValue(name, value)

All other methods have the same names as before.

Note that Entry is now generic (Entry<V>) because nested records in a generic interface are implicitly static and so do not inherit the enclosing interface's type parameter.

Read the specifications for all methods carefully in GenericQueue.java. Note the time complexity requirements on each method: some methods must run in O(1) time, some in O(log n), and some in O(n). The new HW7 interface states an explicit time bound on every method. For methods that already had a bound in HW6, the bound is the same. But many methods that had no stated bound in HW6 now have a required time bound.

Recall that a big-oh time bound such as O(f(n)) means "runs in time proportional to f(n) or better". Thus, a method that runs in constant time also satisfies a specification that requires O(log n).

The interface specification for GenericQueue also requires your implementation to use O(n) space, where n is the number of elements currently in the queue.

A few methods also now explicitly require their name argument(s) to be non-null. HW6 did not state this requirement for all relevant methods; if any of your HW5/HW6 spec tests passed null for a name, you will need to remove those tests or convert them to implementation tests.

Starting from your HW6 solution, implement GenericQueueImpl<V>. You will need to:

  • Add a type parameter <V> to your class and update your code to use V where it previously used String for questions/values.
  • Rename methods to match the updated interface.
  • Ensure all time and space bounds are met.

Meeting all time and space bounds may require redesigning your choice of data structures. We recommend thinking about your data structure choices carefully before you start making changes to your HW6 code.

When you change your code, be sure to update your abstraction function (AF) and representation invariant (RI) comments, and update your checkRep() method to check the new invariants.

Task 2: Test your queue

Copy over your HW6 specification tests into the provided HW7 starter file, being careful to preserve the package declaration from the starter code. Then update your tests to work with the new generic interface. If you changed your data structure or method implementations in Task 1, make sure your tests still cover the updated code correctly.

Your tests should exercise the queue with at least two different value types (e.g., GenericQueue<String> and GenericQueue<Integer>) to verify that the implementation works generically.

As with previous assignments, specification tests must pass on any correct implementation. If you would like to test additional behavior of your implementation that is not guaranteed by the specification, you can do so in the GenericQueueImplTest.java file. The implementation test file will not be graded, and you may leave it blank if you wish.

Task 3: Ticket Buying App

In this task, you will build a small ticket buying web application that simulates a ticket-purchasing experience.

Architecture

In HW6, the server generated complete HTML pages using JTE templates. Every user action submitted an HTML form, the server processed it and rendered a new page, and the browser loaded that new page.

In this assignment, the architecture is different:

  • The backend (still Java/Javalin, but no JTE) exposes a set of JSON API routes. Each route receives a request, processes it, and returns a JSON response (not an HTML page).
  • The frontend is written as a separate HTML file and a JavaScript file (and an optional CSS file for styling). The JavaScript is responsible for sending requests to the backend via fetch, receiving the responses, and updating the page dynamically to show the correct UI.

Starter code walkthrough

The starter code app is a simple timed quiz web app that demonstrates the techniques you will need to build the full ticket buying app.

The rest of this section is collapsed. Click here to expand the walkthrough.Click here to collapse this section.

Run the starter code app with one of these techniques:

  • From the command line, use make run. (You must finish Task 1 before the code will compile.)
  • From VS Code, press the word "Run" near the main() method in VS Code when looking at TicketApp.java.

Then visit http://localhost:7070/ in your browser.

The app loads a page that shows you an "Start quiz" button.

The starter quiz on first load. The user sees the "Start quiz" button.

Pressing "Start quiz" displays a randomly selected question from a small hard-coded bank.

After clicking "Start quiz": the server returned the ice cream question along with a deadline. The intro paragraph and the "Start quiz" button are now hidden so they aren't in the way during the round.

If you type the correct answer (Vanilla) into the text box and press Submit within 5 seconds, then the server will check your answer and tell you whether you got it right.

After typing "Vanilla" and pressing Submit: the server returned "Correct." and the quiz block is hidden again, ready for another round.

Pressing "Start quiz" again starts a new round with a fresh deadline, and possibly a different question. If we submit an incorrect answer this time, the server returns "Wrong." instead.

After getting another question but submitting the wrong answer: the server returned "Wrong."

Another possibility is that the user waits too long to respond. In this case, the server gives a third response: "Late."

After asking, waiting more than 5 seconds, and then submitting: the server returned "Late."

The starter code shows how four files work together:

  • tickets/TicketApp.java is the Javalin backend that defines the static files directory and registers handlers for all the GET and POST requests, which return JSON.
  • static/index.html is the HTML file for the frontend. Note that this file is not a template.
  • static/index.js is the JavaScript file for the frontend. It sends requests to the backend and manipulates the HTML based on the responses.
  • static/style.css is the CSS file for the frontend. We use this to display some parts of the UI in a nicer way. You are welcome to leave this blank or use it however you see fit.

Whenever the user clicks a button, the frontend JavaScript code sends an HTTP request to the server, which is handled by the Java backend, possibly updating its state, which then returns a small JSON response, which is parsed by the frontend JavaScript, which updates the relevant parts of the UI in place. The HTML page itself is only loaded once, on the first page visit.

You should study TicketApp.java, index.html, and index.js together to see how the pieces fit together. Once you are satisfied that you understand it, you should delete the quiz demo and replace it with your real ticket buying application.

Ticket Buying App

The ticket buying app works as follows:

  • The goal of the app is to let users buy tickets.
  • The page where users buy tickets is called the "shopping center". It has a fixed capacity (configurable, default 10) measured in tickets.
  • When a user wants to buy tickets, they first declare how many tickets they intend to buy. The declared amount cannot exceed the shopping center capacity.
  • If there are no other waiting users and there is enough capacity in the shopping center to accept the new user's request, then they are immediately admitted to the shopping center. Otherwise, they are added to the back of the waiting list.
  • The waiting list is served on a first-come, first-served basis. The user at the front of the line is admitted only when their declared number of tickets fits in the currently-free capacity. A small-quantity waiter behind a large-quantity waiter must wait their turn even if they might fit in the shopping center.
  • A waiter may change how many tickets they want (up or down) at any time. Adjusting down may let them fit when they previously didn't.
  • A waiter can also see how many seconds they have been waiting, and this should refresh in some way. (In the staff solution, we have a refresh button. You could also do this with a timer if you want it to tick automatically.)
  • A user on the shopping center page has a deadline (configurable, default 60 seconds) by which they must purchase tickets or leave. The number purchased must be between 1 and their reserved quantity (a user can purchase fewer tickets than they had declared while in line, but they cannot purchase more than this, as that might cause the shopping center's capacity to be exceeded). If the deadline expires, they are kicked out and must re-join (at the back of the line).
  • When a user leaves the shopping center for any reason (purchase, leave, or timeout), their reserved tickets are released. Other users are then admitted from the front of the waiting list one by one as long as there is still capacity.
  • Users also have a "profile" page, accessed by typing in their name, where they can see their past purchases and change their name.
  • The app has an "admin" page (with no authentication, so anyone can view it). It shows the queue length, the total and currently-used capacity, and the next person in line (with their declared quantity). It also has a form to insert a new entry into the line just after a specified existing waiter (carrying that entry's own declared quantity).

Your task is to build a web application with the features described above. More detailed examples of user scenarios are given below. You do not need to match the style or wording of our example interface exactly, but all the same functionality must be present.

Your web app must use the generic queue from task 1 to store the users who are waiting to enter the shopping center. You may also use additional data structures of your choice.

Web app technology

Here are some tips on how to use Java/Javalin in the backend for this assignment:

  • For GET requests, read query parameters with ctx.queryParam(...). For POST requests, define a record matching the JSON body shape and use ctx.bodyAsClass(...) to parse:

    public record JoinBody(String name, int quantity) {}
    // ...
    JoinBody body = ctx.bodyAsClass(JoinBody.class);
    String name = body.name();
    int quantity = body.quantity();
    
    String otherName = ctx.queryParam("name");  // GET query string

    Jackson (bundled with Javalin) deserializes the JSON into the record by matching JSON field names against record component names.

And here are some tips for the JavaScript frontend:

  • To send a GET request with query parameters:

    const resp = await fetch('/api/status?name=' + encodeURIComponent(name));
    const data = await resp.json();

    Use encodeURIComponent() when putting user input into a URL so that special characters are handled correctly.

  • If you need to build an HTML list of unknown length, you can create elements dynamically in JavaScript:

    const li = document.createElement('li');
    li.textContent = '2 tickets on 5/20/2026';
    purchaseList.appendChild(li);
  • You can have multiple "pages" and switch between them by marking all but one of them .hidden.

See the guide on Web Development with JavaScript for more information about how to build this style of web application.

Other tips about the ticket buying app:

  • Shoppers who exceed the time limit must be removed from the shopping center, and users waiting in line should then be allowed to enter in order as long as there is spare capacity. There are different valid approaches to implementing this. A simple approach that does not require background timers or threads is to evict and promote at the beginning of every request handler on the backend.
  • Your application should handle all inputs reasonably. For example:
    • If a user tries to join with a name that is already in the queue, the API should return an error message.
    • If a user tries to purchase when they are not in the shopping center, the API should return an error message.
    • If a required parameter (like a name) is missing, the API should return an error message.
  • Your backend should never crash or return a Java stack trace, even for "invalid" inputs. Your frontend should display error messages from the API to the user in a clear way (via the developer console is fine; via the UI is even better, but not required).

Example scenarios

Here are a few example scenarios to demonstrate how the application works. Remember that your app does not need to look exactly like ours, but it should have the same logical behavior.

The rest of this section is collapsed. Click here to expand the scenarios.Click here to collapse this section.

Scenario A: The first user joins, shops, and buys

The first user types their name (alice) and desired ticket count (2); then they click Join.

The initial join view. The user picks a name and the number of tickets they want to buy.

Since they are the first user to arrive, the shopping center is empty, so they are immediately granted access to the shopping center. The page displays the amount of time they have before being kicked out, their name (at the top), and the number of tickets they reserved. They can finalize their decision about how many tickets they want (but it must be less than or equal to their reserved number of tickets).

alice is admitted to the shopping center with a 2-ticket reservation. The purchase quantity is capped at her reserved number of tickets.

Clicking Purchase records the sale on the server and displays the homepage with a message banner confirming their purchase. (The message banner is cleared the next time the user clicks a button or refreshes.)

After clicking Purchase: the buy is recorded, the user returns to the join view with a confirmation banner.

Clicking the profile button and entering their name shows the list of tickets they have purchased and when.

alice's profile, looked up from the Profile tab. The purchase has been recorded with its timestamp and quantity.

Scenario B: When the shopping center gets full

The shopping center has a capacity of 10 tickets. Suppose two people are already shopping: bob with 2 tickets, and cathy with 3 tickets. This means 5 tickets are currently reserved, which leaves 5 free.

Two more users then join in this order:

  • drew wants 6 tickets. 6 > 5 free, so drew goes to the back of the line.
  • erin wants 1 ticket. erin would fit in the 5 free, but because users are served in order, erin must wait behind drew. She does not skip ahead even though she individually fits within the free capacity.

Clicking on the admin button shows the resulting state. 2 shoppers using 5 tickets, 2 waiters wanting 7 tickets total, and drew at the front of the line:

Admin view of the final state. drew is at the front but blocked by capacity; erin is behind drew, and must wait because drew arrived first.

drew's own view shows position 1, the 6-ticket reservation, and the adjust form:

drew's queue view: at position #1, wants 6 tickets. The Adjust form lets them change their declared quantity while waiting.

erin's view shows position 2. She's stuck not by capacity but by drew being in front of her:

erin's queue view: at position #2, wants 1 ticket. The single ticket would fit in the free capacity, but strict FIFO keeps her behind drew.

Now suppose drew adjusts his requested reservation down to 4 tickets. Since the waiting room has room for this, drew is then immediately admitted. The shopping center now contains reservations for 9 tickets, leaving 1 ticket still free. So erin is admitted as well. The shopping center is at capacity.

The admin view shows the resulting state.

After drew's downward adjustment, both drew and erin are admitted to the shopping center, which is now full.

Scenario C: Admin inserts into the line

The admin view also lets staff splice a new entry into the line just after an existing waiter, perhaps if a VIP comes along. The inserted entry carries its own declared quantity and is gated by the same capacity logic as any other waiter.

Suppose frank is in the shopping center with 4 tickets reserved, gina is waiting trying to reserve 7 tickets. The admin types "vip", "gina", and 1 into the Insert form:

Admin view with the Insert form filled in. gina is blocking 1 waiter slot; the admin is about to splice vip in behind her.

Clicking Insert places vip at position 2, right behind gina. The admin view refreshes to show the updated line. Note that vip still has to wait: even though there's plenty of capacity for 1 ticket, gina is in front and blocks the line.

After clicking Insert: vip is spliced in at position 2, behind gina. Total tickets wanted in the line is now 8.

Correctness, code quality, and AI

The same correctness and code quality and AI guidelines from HW5 and HW6 apply to this assignment.

You do not need to write JUnit tests for the web application. Course staff will grade the web application by running it and trying it themselves.

Reflection

Submit a short text file called REFLECTION.txt in the top level directory answering the questions below.

  1. Briefly describe what changes you needed to make to your HW6 code to support a generic value type and the new time and space requirements.
  2. Compare the HW6 web application (server-rendered HTML, no JavaScript) with the HW7 web application (JSON API and JavaScript). Which approach did you find easier to work with as a developer, and why? Do you think users can tell the difference between the two approaches?
  3. Did you use AI assistance on this assignment? If yes, which tool(s) and for which parts?
  4. Anything else you would like us to know about how this assignment went for you?

Submission

Submit the following files to the Homework 7 assignment on Gradescope:

  • GenericQueueImpl.java
  • GenericQueueSpecTest.java
  • GenericQueueImplTest.java
  • TicketApp.java
  • Your static frontend files (HTML, JavaScript, CSS)
  • REFLECTION.txt

You can either upload these files individually on Gradescope, or run make submission to bundle them into a submission.zip and upload that.

You should not modify or submit GenericQueue.java.

We have set up a Gradescope autograder that will compile and run your code. It will run your own tests on your own code and show you the results. It will also run a small number of very basic staff tests to make sure that your code compiles and has the required features. Passing all the autograder tests does not guarantee your code is correct or high quality.