package edu.umd.rhsmith.diads.meater.util; public abstract class ControlUnit extends LogUnit { // TODO either replace this system with something more reliable and easy to // use, or remove it and let the user know there are no guarantees if they // set up and start/stop units in different threads. private final Object controlWaiter; protected final Object controlLock; private boolean stopBegun; private boolean startBegun; private boolean stopFinished; private boolean startFinished; private boolean startFailed; private ControlException failureException; public ControlUnit() { this.controlWaiter = new Object(); this.controlLock = new Object(); this.startBegun = false; this.startFinished = false; this.stopBegun = false; this.stopFinished = false; this.startFailed = false; this.failureException = null; } /* * -------------------------------- * Control commands * -------------------------------- */ public final void start() throws ControlException { synchronized (this.controlLock) { this.requireUnStarted(); this.requireUnStopped(); this.getLogger().info(this.messageString(MSG_STARTING)); synchronized (this.controlWaiter) { this.startBegun = true; this.controlWaiter.notifyAll(); } try { this.doStartupRoutine(); } catch (ControlException e) { this.getLogger().info(this.messageString(MSG_START_FAILED)); synchronized (this.controlWaiter) { this.startFailed = true; this.failureException = e; this.controlWaiter.notifyAll(); } throw e; } this.getLogger().info(this.messageString(MSG_STARTED)); synchronized (this.controlWaiter) { this.startFinished = true; this.controlWaiter.notifyAll(); } } } protected abstract void doStartupRoutine() throws ControlException; public final void stop() { synchronized (this.controlLock) { synchronized (this.controlWaiter) { this.stopBegun = true; this.getLogger().info(this.messageString(MSG_STOPPING)); this.controlWaiter.notifyAll(); } this.doShutdownRoutine(); synchronized (this.controlWaiter) { this.stopFinished = true; this.getLogger().info(this.messageString(MSG_STOPPED)); this.controlWaiter.notifyAll(); } } } protected abstract void doShutdownRoutine(); /* * -------------------------------- * Control status-checkers * -------------------------------- */ public final boolean isStartBegun() { return startBegun; } public final boolean isStartFinished() { return startFinished; } public final boolean isStopBegun() { return stopBegun; } public final boolean isStopFinished() { return stopFinished; } public final boolean isStartFailed() { return startFailed; } /* * -------------------------------- * Control status-waiters * -------------------------------- */ public final void awaitStartBegun() throws InterruptedException { synchronized (this.controlWaiter) { while (!this.startBegun) { this.controlWaiter.wait(); } } } public final void awaitStopBegun() throws InterruptedException { synchronized (this.controlWaiter) { while (!this.stopBegun) { this.controlWaiter.wait(); } } } public final void awaitStartFinished() throws InterruptedException, ControlException { synchronized (this.controlWaiter) { while (!this.startFinished && !startFailed) { this.controlWaiter.wait(); } if (startFailed) { throw failureException; } } } public final void awaitStopFinished() throws InterruptedException { synchronized (this.controlWaiter) { while (!this.stopFinished) { this.controlWaiter.wait(); } } } /* * -------------------------------- * Control state-validators * -------------------------------- */ public final void requireStarted() throws IllegalStateException { if (!this.startFinished) { throw new IllegalStateException(this.messageString(MSG_ERR_NOSTART)); } } public final void requireUnStarted() throws IllegalStateException { if (this.startBegun) { throw new IllegalStateException(this.messageString(MSG_ERR_START)); } } public final void requireStopped() throws IllegalStateException { if (!this.stopFinished) { throw new IllegalStateException(this.messageString(MSG_ERR_NOSTOP)); } } public final void requireUnStopped() throws IllegalStateException { if (this.stopBegun) { throw new IllegalStateException(this.messageString(MSG_ERR_STOP)); } } /* * -------------------------------- * General getters / setters * -------------------------------- */ public String getControlStatusString() { if (!this.isStartBegun()) { return "Startup not begun"; } else if (!this.isStartFinished()) { return "Startup in progress"; } else if (!this.isStopBegun()) { return "Active"; } else if (!this.isStopFinished()) { return "Shutdown in progress"; } else { return "Terminated"; } } /* * -------------------------------- * Messages * -------------------------------- */ private static final String MSG_ERR_NOSTART = "not yet started"; private static final String MSG_ERR_START = "already starting / started"; private static final String MSG_STARTING = "starting"; private static final String MSG_STARTED = "started"; private static final String MSG_START_FAILED = "start failed"; private static final String MSG_ERR_NOSTOP = "not yet stopped"; private static final String MSG_ERR_STOP = "already stopping / stopped"; private static final String MSG_STOPPING = "stopping"; private static final String MSG_STOPPED = "stopped"; }