On this page:
5.1 Dawdling Clients
5.2 Handoff
5.2.1 Clock Service
5.3 Updated Initialization
5.4 Testing Task
5.5 Interfaces
8.9

5 — It’s About Time

Due Thursday, 15 February 2024, 11:59:59pm

Delivery Place the product of this week’s milestone into your git repo as follows:

Your project should generate a JAR named Milestone5.jar (see Java, Maven; there is no need to specify a mainClass for this milestone) including the Main and EagerClientFactory classes described below. Building the project (such as with mvn package) should put the executable in a folder named target at the top level of your repo.

Purpose

The purpose of this milestone is to bring together the pieces of the HALP center that you have been working on over the last several weeks and the role that time plays in its operation. There will be three major new pieces of functionality:

5.1 Dawdling Clients

Our system has a problem: if a client takes an animal out to play, or reserves a particular appointment spot, nothing makes sure that they make a decision in a timely manner. As a result, other patrons may be deprived the opportunity to adopt a pet or book an appointment.

To address the issue, we will implement a timeout. If a client takes a pet out to play and doesn’t return or adopt it within a specified amount of time, the shelter (or a representative of) returns the pet to the pen. Likewise, when a client finalizes a particular vet, date, and time, but doesn’t try to book the appointment in a timely manner, they should lose their right to that spot.

Exactly how long a client has to make these decisions will be parameters to the system (see Updated Initialization).

Of course, the client should probably be notified when such an action is taken. Consequently, we will extend our notion of a client from an entity that initiates all communications by making requests (calling methods) to one that can receive notifications as well. We will extend the ShelterEntry and ClinicEntry interfaces to allow a client to provide their "callback" information:

To support previous milestones, the old methods that don’t take a notification receiver should still enforce the timeout but do not need to inform the client.

  public interface ShelterEntry {

    PetViewer viewAnimals();

    PetViewer viewAnimals(ClientNotificationReceiver clientNotificationReceiver);

  }

  

  public interface ClinicEntry {

    AppointmentScheduler requestAppointment(AdoptablePet pet);

  

    AppointmentScheduler requestAppointment(AdoptablePet pet, ClientNotificationReceiver clientNotificationReceiver);

  }

The ClientNotificationReceiver defines the communications we might need to tell clients:

  public interface ClientNotificationReceiver {

    void playTimeout();

    void appointmentTimeout();

    void scheduleAnAppointment(AppointmentScheduler scheduler);

  }

Implementation Task. Update your adoption and appointment scheduling implementations to enforce timeouts. Arrange to call the playTimeout or appointmentTimeout methods, respectively, on the client receiver (when provided).

5.2 Handoff

Some animals at the clinic haven’t yet received their standard immunizations—rabies, bordatella, etc. It’s fine for a client to adopt such a pet, but we want to make sure they bring their new best friend back to the clinic to get their shots.

To make sure this happens, a client adopting a pet that has not received their vaccinations will be prompted to first schedule an appointment at the clinic. The adoption completes if and only if the client successfully books such an appointment in a timely manner. If the client fails to book an appointment (again, in a timely manner), the adoption attempt fails.

Specifically, when the scenario of adopting an unvaccinated pet arises, your implementation will call the scheduleAnAppointment method of the corresponding ClientNotificationReceiver with an AppointmentScheduler. The client then has a certain amount of time (see Updated Initialization) to finalize an appointment, otherwise the adoption request fails.

Implementation Task. Extend your PetViewer implementation to collaborate in the specified manner with the clinic.

5.2.1 Clock Service

To make it feasible to run the system, we will abstract the notion of time advancing from real "wall clock" time. To simulate time progressing through the day and weeks, we will implement a Clock Service. The purpose of the clock service is to notify any interested parties of the important times in the clinic’s day:

You may wish to employ a scheduled task to implement this behavior.

The clock service is an instance of the observer pattern. Its interface allows objects to subscribe to notifications:

  public interface ClockService {

    DayOfWeek listen(ClockListener listener);

    void stopListening(ClockListener listener);

    void start();

    void stop();

  }

The methods of a ClockListener correspond to the events defined above:

  public interface ClockListener {

    void startDay(DayOfWeek day);

    void itIsNow(LocalTime time);

    void finishDay(DayOfWeek day);

  }

A component subscribes to updates via the listen method, which returns the current DayOfWeek. Until that listener calls stopListening to unsubscribe, it receives periodic updates about the day and time in the form of method calls. For example, a ClockListener may receive the following sequence of method calls:

  1. startDay(Friday)

  2. itIsNow(8am)

  3. itIsNow(8:30am)

  4. ...

  5. itIsNow(5:30pm)

  6. itIsNow(6:00pm)

  7. finishDay(Friday)

  8. startDay(Saturday)

  9. itIsNow(9am)

  10. ...

The service should insulate itself as much as possible from the workings of its users. In particular, one buggy or slow user should not degrade the quality of service to the rest of the users.

Implementation Task. Implement two variants of the ClockListener service:
  • One that progresses at a fixed rate. That is, in the example method trace above, (roughly) the same amount of absolute time passes between each method call.

  • One controlled by an external entity. Such a "puppet" clock is useful for testing. The clock service makes the same sequence of method calls to its listeners, but only progresses to the next point in the sequence when a controller calls its nextStep method.

Create a class named ClockServiceFactory in the com.neu.halp.clock package. The class should define (at least) the following two methods:

  public static ClockService newTimedClockService(int millisecondsBetweenEvents) {

      ...

  }

  

  public static PuppetClockService newPuppetClockService() {

      ...

  }

5.3 Updated Initialization

Extend your com.neu.halp.center.Main class with a method that has the following signature:

  public static CenterClientEntry initializeCenter(Reader configReader,

                                                   int clientIdleTimeoutMilliseconds,

                                                   ExecutorService threadPool) {

    ...

  }

The initialization method specifies the timeout to use, in milliseconds, for the behaviors specified above. It also passes in an ExecutorService that should be used for all concurrent tasks initiated by the center.

As we’ve seen, Java best practices discourages creating and starting instances of the Thread class. Update your project, replacing such uses of Thread with Runnable, Callable, or similar. Use the thread pool (ExecutorService) to initiate concurrent tasks rather than Thread::start.

5.4 Testing Task

With the new vaccination appointment requirement of adoptions, we need to update our eager TestClients. To remedy the situation, we will combine the behaviors of our eager and scheduling clients. If the shelter asks the eager client to schedule an appointment, it should make an appointment following the dictates of the basic scheduling behavior (Testing Task).

First, the updated TestClient interface now includes the following method to get the scheduled appointment, if any:

  Optional<Appointment> getAppointment();

Implementation Task.

TestClients created with the newClient(int patience) method may simply fail to schedule an appointment.

Next, define a method with the following signature in the EagerClientFactory class inside the com.neu.halp.test package for creating test clients with this combined behavior:

  public TestClient newClient(int patience,

                              Function<Collection<Vet>,Collection<Vet>> vetPreference,

                              Function<Collection<Integer>,Collection<Integer>> weekPreference,

                              Function<Collection<DayOfWeek>,Collection<DayOfWeek>> dayPreference,

                              Function<Collection<LocalTime>,Collection<LocalTime>> timePreference) {

    ...

  }

5.5 Interfaces

Place the new and updated interfaces in the designated package(s) in your project. Do not modify them.