/** * Commit seats held for a specific customer * * @param seatHoldId the seat hold identifier * @param customerEmail the email address of the customer to which the seat hold is assigned * @return a reservation confirmation code */ @PUT @Timed @ExceptionMetered @Path("/reserveSeats") @Produces({MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON}) public String reserveSeats( @QueryParam("seatHoldId") @NotNull Integer seatHoldId, @Email @NotBlank String customerEmail) throws InvalidSeatHoldRequestException, ReservationNotFoundException { validateCustomerEmail(customerEmail); Integer seatLevelId = holdId2SeatLevel.get(seatHoldId); if (seatLevelId == null) { throw new InvalidSeatHoldRequestException( String.format("Seat hold #%d does not exist in the venue", seatHoldId)); } ReservationManager resource = seatLevelResourceManager[seatLevelId]; ReservationHold hold = resource.getReservation(seatHoldId); if (hold == null) { throw new ReservationNotFoundException(seatHoldId, customerEmail); } SeatHold seatHold = buildSeatHold(hold); if (!Objects.equals(seatHold.getCustomerEmail(), customerEmail)) { throw new InvalidSeatHoldRequestException( String.format( "Seat hold #%d is not associated with customer email %s", seatHoldId, customerEmail)); } if (!resource.confirmHold(seatHold.getReservationId())) { throw new ReservationNotFoundException(seatHoldId, customerEmail); } return generateReservationCode(seatHoldId, customerEmail); }
/** * The number of seats in the requested level that are neither held nor reserved * * @param venueLevel a numeric venue level identifier to limit the search * @return the number of tickets available on the provided level * @throws VenueLevelNotFoundException */ @GET @Timed @ExceptionMetered @Path("/numSeatsAvailable") @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) public int numSeatsAvailable(@QueryParam("venueLevel") Integer venueLevel) throws VenueLevelNotFoundException { int fromLevel = 0; int toLevel = seatLevelResourceManager.length - 1; if (venueLevel != null) { if (venueLevel < 0 || venueLevel >= seatLevelResourceManager.length) { throw new VenueLevelNotFoundException(venueLevel); } fromLevel = venueLevel; toLevel = venueLevel; } int available = 0; for (; fromLevel <= toLevel; ++fromLevel) { ReservationManager resource = seatLevelResourceManager[fromLevel]; available += resource.countAvailableSlots(); } return available; }
/** * Find and hold the best available seats for a customer * * @param numSeats the number of seats to find and hold * @param minLevel the minimum venue level * @param maxLevel the maximum venue level * @param customerEmail unique identifier for the customer * @return a SeatHold object identifying the specific seats and related information * @throws InvalidSeatHoldRequestException, NoSeatsAvailableException */ @POST @Timed @ExceptionMetered @Path("/findAndHoldSeats") public SeatHold findAndHoldSeats( @QueryParam("numSeats") @NotNull Integer numSeats, @QueryParam("minLevel") Integer minLevel, @QueryParam("maxLevel") Integer maxLevel, @Email @NotNull @NotBlank String customerEmail) throws InvalidSeatHoldRequestException, NoSeatsAvailableException { if (numSeats == 0) { return null; } if (numSeats < 0) { throw new InvalidSeatHoldRequestException( String.format("Invalid number of hold [%d] being requested", numSeats)); } int lo = minLevel != null ? minLevel : 0; int hi = maxLevel != null ? maxLevel : seatLevelResourceManager.length - 1; if (lo > hi || lo < 0 || hi >= seatLevelResourceManager.length) { throw new InvalidSeatHoldRequestException( String.format("Seat hold level constraints are not valid [%d, %d]", lo, hi)); } validateCustomerEmail(customerEmail); for (; hi >= lo; --hi) { ReservationManager resource = seatLevelResourceManager[hi]; ReservationHold hold = resource.requestHold(numSeats, customerEmail); if (hold != null) { SeatHold seatHold = buildSeatHold(hold); holdId2SeatLevel.put(seatHold.getReservationId(), seatHold.getLevel()); return seatHold; } } throw new NoSeatsAvailableException(); }
/** * The details for reservation holds at the venue * * @return * @throws VenueLevelNotFoundException */ @GET @Timed @ExceptionMetered @Path("/holdDetails") public List<SeatLevelHoldDetail> venueHoldDetails(@QueryParam("venueLevel") Integer venueLevel) throws VenueLevelNotFoundException { ArrayList<SeatLevelHoldDetail> seatLevelHoldDetails = new ArrayList<>(); int fromLevel = 0; int toLevel = seatLevelResourceManager.length - 1; if (venueLevel != null) { if (venueLevel < 0 || venueLevel >= seatLevelResourceManager.length) { throw new VenueLevelNotFoundException(venueLevel); } fromLevel = venueLevel; toLevel = venueLevel; } for (; fromLevel <= toLevel; ++fromLevel) { ReservationManager resource = seatLevelResourceManager[fromLevel]; SeatLevel seatLevel = configuration.getSeatLevels().get(fromLevel); List<SeatHold> seatHolds = resource .getAllReservations() .stream() .map(this::buildSeatHold) .collect(Collectors.toList()); seatLevelHoldDetails.add( new SeatLevelHoldDetail( seatLevel.getName(), seatLevel.getRows(), seatLevel.getSeatsInRow(), seatHolds)); } return seatLevelHoldDetails; }
/** * Initialize the venue seat reservation manager * * @param configuration * @throws Exception */ public VenueTicketManager(VenueSeatingConfiguration configuration) throws Exception { if (configuration.getSeatLevels().size() == 0) { throw new IllegalArgumentException("configuration.seatLevels"); } this.configuration = configuration; this.seatLevelResourceManager = new ReservationManager[configuration.getSeatLevels().size()]; for (int lvlNum = 0; lvlNum < configuration.getSeatLevels().size(); ++lvlNum) { SeatLevel lvl = configuration.getSeatLevels().get(lvlNum); if (lvl.getRows() < 1) { throw new Exception( String.format( "Invalid number of rows (%d) for seating level %d", lvl.getRows(), lvlNum)); } if (lvl.getSeatsInRow() < 1) { throw new Exception( String.format( "Invalid number of seats (%d) for seating level %d, row %d", lvlNum, lvl.getRows(), lvl.getSeatsInRow())); } int capacity = lvl.getRows() * lvl.getSeatsInRow(); ReservationManager resource = new ReservationManager( lvlNum, capacity, new HoldTimeStampProvider(configuration.getHoldsExpireAfter()), lvl.getName()); resource.setReservationIdCounter(reservationIdCounter); this.seatLevelResourceManager[lvlNum] = resource; } }