Fix: UI now has command queue of size 1.
A new CommandPend class is introduced. It functions basically as an ArrayBlockingQueue of size 1. Difference is that it can handle two functions from two different threads - trample(cmd): removes the previously kept command (if there is such) and overrides it with the next command - offer(cmd): keeps the given command, but only if it doesn't currently keeps an older one This new functionality is used so the UI can get commands from the controller (but only take into account the latest one). At the same time it gets tick commands from its clock ticker, but only keep and handle those if it doesn't have a real controller command to handle.mixer
parent
1cf16d8386
commit
da7a05ecd8
|
@ -0,0 +1,53 @@
|
|||
package meerkat.voting.ui;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
|
||||
/**
|
||||
* A special kind of an ArrayBlockingQueue.
|
||||
* It is only of size 1, meaning it keeps only one element at most at every point of time.
|
||||
* The trample function is similar to put/add except that it overrides the previously kept element.
|
||||
* Other functions are similar to the matching functions in ArrayBlockingQueue.
|
||||
* For instance, the offer function only "recommends" another element to keep, but if there is a stored element
|
||||
* already, than this recommendation is ignored.
|
||||
*/
|
||||
public class CommandPend<T> {
|
||||
|
||||
private ArrayBlockingQueue<T> queue;
|
||||
|
||||
public CommandPend () {
|
||||
queue = new ArrayBlockingQueue<>(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* overrides the current kept command
|
||||
* @param cmd a command to override the previous one (if existed)
|
||||
*/
|
||||
synchronized public void trample (T cmd) {
|
||||
queue.clear();
|
||||
queue.add(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* keeps the offered command, but only if there is no other command to handle right now
|
||||
* @param cmd a command to keep if we currently do not have another
|
||||
*/
|
||||
synchronized public void offer (T cmd) {
|
||||
queue.offer(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves and removes the kept command, waiting if necessary until a command becomes available.
|
||||
* @return the kept command
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public T take() throws InterruptedException {
|
||||
return queue.take();
|
||||
}
|
||||
|
||||
/**
|
||||
* removes the kept command
|
||||
*/
|
||||
synchronized public void clear () {
|
||||
queue.clear();
|
||||
}
|
||||
}
|
|
@ -8,7 +8,6 @@ import java.io.BufferedReader;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import meerkat.voting.controller.callbacks.*;
|
||||
import meerkat.voting.ui.uicommands.*;
|
||||
|
@ -27,7 +26,7 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
|
|||
private static final Logger logger = LoggerFactory.getLogger(SystemConsoleUI.class);
|
||||
|
||||
private BufferedReader bufferedReader;
|
||||
private LinkedBlockingQueue<UICommand> queue;
|
||||
private CommandPend<UICommand> cmdPend;
|
||||
private Date startWaitingTime;
|
||||
|
||||
private volatile boolean shutDownHasBeenCalled;
|
||||
|
@ -37,30 +36,30 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
|
|||
final int tickDurationInMillisec = 10; // period between view update calls
|
||||
|
||||
logger.info("A VB UI console is constructed");
|
||||
queue = new LinkedBlockingQueue<>();
|
||||
cmdPend = new CommandPend<>();
|
||||
bufferedReader = new BufferedReader(new InputStreamReader(in));
|
||||
|
||||
startWaitingTime = null;
|
||||
Timer timer = new Timer();
|
||||
timer.scheduleAtFixedRate(new TickerTimerTask(queue), new Date(), tickDurationInMillisec);
|
||||
timer.scheduleAtFixedRate(new TickerTimerTask(cmdPend), new Date(), tickDurationInMillisec);
|
||||
|
||||
shutDownHasBeenCalled = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* the run() method. Simply loops and takes commands from the UI's queue and handles them accordingly
|
||||
* the run() method. Simply loops and takes the UI's pending command and handles it accordingly
|
||||
*/
|
||||
@Override
|
||||
public void run () {
|
||||
logger.info("UI starts running");
|
||||
while (! wasShutDownCalled()) {
|
||||
try {
|
||||
UICommand command = queue.take();
|
||||
UICommand command = cmdPend.take();
|
||||
handleSingleCommand(command);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
logger.warn ("Interrupted while reading from command queue " + e);
|
||||
logger.warn ("Interrupted while reading the pending command " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +69,8 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
|
|||
public void callShutDown() {
|
||||
logger.info("callShutDown command has been called");
|
||||
shutDownHasBeenCalled = true;
|
||||
queue.clear();
|
||||
stopWaiting();
|
||||
cmdPend.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,7 +124,7 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
|
|||
@Override
|
||||
public void startNewVoterSession(FutureCallback<Void> callback) {
|
||||
logger.debug("UI interface call to startNewVoterSession");
|
||||
queue.add(new StartSessionUICommand((NewVoterCallback)callback));
|
||||
cmdPend.trample(new StartSessionUICommand((NewVoterCallback)callback));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -178,8 +178,7 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
|
|||
@Override
|
||||
public void chooseChannel(List<BallotQuestion> questions, FutureCallback<List<BallotAnswer>> callback) {
|
||||
logger.debug("UI interface call to chooseChannel");
|
||||
ChannelChoiceUICommand command = new ChannelChoiceUICommand(questions, (ChannelChoiceCallback)callback);
|
||||
queue.add(command);
|
||||
cmdPend.trample(new ChannelChoiceUICommand(questions, (ChannelChoiceCallback)callback));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -212,7 +211,7 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
|
|||
@Override
|
||||
public void askVoterQuestions(List<BallotQuestion> questions, FutureCallback<List<BallotAnswer>> callback) {
|
||||
logger.debug("UI interface call to chooseChannel");
|
||||
queue.add(new RaceVotingUICommand(questions, (VotingCallback)callback));
|
||||
cmdPend.trample(new RaceVotingUICommand(questions, (VotingCallback)callback));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -244,7 +243,7 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
|
|||
@Override
|
||||
public void castOrAudit(FutureCallback<FinalizeBallotChoice> callback) {
|
||||
logger.debug("UI interface call to castOrAudit");
|
||||
queue.add(new CastOrAuditUICommand((CastOrAuditCallback)callback));
|
||||
cmdPend.trample(new CastOrAuditUICommand((CastOrAuditCallback)callback));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -288,7 +287,7 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
|
|||
@Override
|
||||
public void notifyVoterToWaitForFinish(UIElement message, FutureCallback<Void> callback) {
|
||||
logger.debug("UI interface call to notifyVoterToWaitForFinish");
|
||||
queue.add(new WaitForFinishUICommand(message, (WaitForFinishCallback)callback));
|
||||
cmdPend.trample(new WaitForFinishUICommand(message, (WaitForFinishCallback)callback));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -331,8 +330,7 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
|
|||
@Override
|
||||
public void showErrorMessageWithButtons(UIElement errorMessage, UIElement[] buttonLabels, FutureCallback<Integer> callback) {
|
||||
logger.debug("UI interface call to showErrorMessageWithButtons");
|
||||
queue.clear();
|
||||
queue.add(new FatalErrorUICommand(errorMessage, buttonLabels, (ControllerCallback<Integer>)callback));
|
||||
cmdPend.trample(new FatalErrorUICommand(errorMessage, buttonLabels, (ControllerCallback<Integer>)callback));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,22 +4,18 @@ import meerkat.voting.ui.uicommands.TickCommand;
|
|||
import meerkat.voting.ui.uicommands.UICommand;
|
||||
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* A thread that sends the UI clock TickCommands in a given constant frequency
|
||||
*/
|
||||
class TickerTimerTask extends TimerTask {
|
||||
private LinkedBlockingQueue<UICommand> uiQueue;
|
||||
public TickerTimerTask (LinkedBlockingQueue<UICommand> uiQueue) {
|
||||
this.uiQueue = uiQueue;
|
||||
private CommandPend<UICommand> cmdPend;
|
||||
public TickerTimerTask (CommandPend<UICommand> cmdPend) {
|
||||
this.cmdPend = cmdPend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
UICommand t = uiQueue.peek();
|
||||
if ((t != null) && !(t instanceof TickCommand)) {
|
||||
uiQueue.add(new TickCommand(null));
|
||||
}
|
||||
cmdPend.offer(new TickCommand(null));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue