Part 1 recap: events, parsing, exceptions, serialize & restore
The non-concurrent half: parameterized events, parsing examples*.txt with Scanner + regex, a custom ControllerException, an orderly shutdown that logs to error.log, serializing the whole controller to dump.out, and a Restore that resumes from it via a Fixable factory.
What Part 1 adds to the skeleton
The starter GreenhouseControls hard-codes its schedule. Part 1 turns it into a real, fault-tolerant program by adding six things, each a topic from earlier modules made concrete.
1. Parameterized events
The starter Bell rings once. Part 1 makes it ring rings times — and the event files declare it: Event=Bell,time=9000,rings=5 (see examples2.txt). So Bell gains a second constructor argument and a loop in action():
public class Bell extends Event {
private int rings;
public Bell(long delayTime, int rings) {
super(delayTime);
this.rings = rings;
}
public void action() {
for (int i = 0; i < rings; i++) System.out.println("Bing!");
}
}
examples2.txt also introduces FansOn / FansOff — proving the framework extends by adding an inner Event subclass, never by editing the controller.
2. Parse the event file with Scanner + regex
Each line looks like Event=LightOn,time=2000 (optionally ,rings=5). You read the file with a Scanner and pull the fields with a capturing regex (Module 4): a group for the event name, a group for the time, an optional group for rings. The captured name string is what later (Part 2) feeds reflection.
Pattern p = Pattern.compile("Event=(\\w+),time=(\\d+)(?:,rings=(\\d+))?");
Matcher m = p.matcher(line);
if (m.matches()) {
String name = m.group(1);
long time = Long.parseLong(m.group(2));
// m.group(3) is null when ,rings= is absent
}
3. A custom checked exception
A fault — a stuck window, a power cut — is a recoverable condition the caller must handle, so it is a checked exception (extends Exception, Module 3):
public class ControllerException extends Exception {
public ControllerException(String why) { super(why); }
}
The new fault events throw it: WindowMalfunction and PowerOut (added in examples3.txt / examples4.txt) throw new ControllerException(...) from action().
4. Orderly shutdown that logs
When run() catches a ControllerException, it does not crash — it calls shutdown(), which records the failure to error.log (append mode + a timestamp, Module 8) and then exits cleanly. This is the fault half of the project.
5. Serialize the whole controller
GreenhouseControls is made Serializable, so on certain faults the program can write its entire object graph — controller, event list, current state — to dump.out with an ObjectOutputStream (Module 8). The clock-related fields are marked transient (absolute times are meaningless after a reload).
6. Restore from the dump
Run with -d dump.out and a Restore path deserializes that file with an ObjectInputStream, calls start() on each surviving event to re-base its time onto now, and resumes the run() loop — the program picks up where it crashed.
The Fixable factory ties it together
Faults map to fixes through an interface used as a factory (Module 2.3). A Fixable declares fix() and log(); a getFixable(int errorcode) method returns the right Fixable for each error code — so adding a new fault is adding a case, not rewriting the recovery logic:
public interface Fixable {
void fix();
void log();
}