Many fixes, some are still only in temporary phase, according to what Arbel told me to do so far.

vbdev2
Hai Brenner 2016-05-23 14:43:01 +03:00
parent c04ed42dca
commit 94f3920e6d
32 changed files with 571 additions and 444 deletions

View File

@ -0,0 +1,84 @@
package meerkat.voting.controller;
import com.google.protobuf.ByteString;
import meerkat.protobuf.Voting.*;
/**
* Created by hai on 18/05/16.
*/
final public class SystemMessages {
private SystemMessages() {
// This is a static class. No instantiation of this is needed.
}
public static UIElement getWaitForCommitMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while committing to ballot"))
.build();
}
public static UIElement getWaitForAuditMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while auditing your ballot"))
.build();
}
public static UIElement getWaitForCastMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while finalizing your ballot for voting"))
.build();
}
public static UIElement getFatalForceRestartMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Fatal error: Internal controller queue received unrecognized command. Force restarting the voting process."))
.build();
}
public static UIElement getRestartVotingButton() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Restart voting"))
.build();
}
public static UIElement getUnrecognizedFinalizeResponseMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Could not understand response for Cast or Audit. Force restarting."))
.build();
}
public static UIElement getUnsuccessfulChannelChoiceMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Choice of channel was unsuccessful. Force restarting."))
.build();
}
public static UIElement getOutputDeviceFailureMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Ballot output device failure. Force restarting."))
.build();
}
public static UIElement getUnsuccessfulVotingMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Voting was unsuccessful. Force restarting."))
.build();
}
public static UIElement getSomethingWrongMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Something was terribly wrong. Force restarting."))
.build();
}
}

View File

@ -8,12 +8,13 @@ import meerkat.voting.output.BallotOutputDevice;
import meerkat.voting.storage.StorageManager;
import java.util.ArrayList;
import java.util.List;
/**
* An interface for the controller component of the voting booth
*/
public interface VotingBoothController {
public interface VotingBoothController extends Runnable{
/**
* initialize by setting all the different components of the Voting Booth to be recognized by this controller
@ -31,8 +32,7 @@ public interface VotingBoothController {
* set the voting questions
* @param questions
*/
public void setBallotChannelChoiceQuestions(BallotQuestion[] questions);
public void setBallotChannelChoiceQuestions(ArrayList<BallotQuestion> questions);
public void setBallotChannelChoiceQuestions(List<BallotQuestion> questions);
/**
* Set the channel question-selector (the component which matches the ballot questions to each user)
@ -44,18 +44,12 @@ public interface VotingBoothController {
* set the voting race questions
* @param questions
*/
public void setBallotRaceQuestions(BallotQuestion[] questions);
public void setBallotRaceQuestions(ArrayList<BallotQuestion> questions);
/**
* a synchronous function. Starts running the controller component, and basically run the whole system for voting
*/
public void run ();
public void setBallotRaceQuestions(List<BallotQuestion> questions);
/**
* an asynchronous call from Admin Console (If there is such one implemented) to shut down the system
*/
public void shutDown();
public void callShutDown();
}

View File

@ -12,20 +12,20 @@ import meerkat.voting.ui.VotingBoothUI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Created by hai on 28/03/16.
*/
public class VotingBoothImpl implements VotingBoothController, Runnable {
public class VotingBoothImpl implements VotingBoothController {
private BallotOutputDevice outputDevice;
private VotingBoothEncryptor encryptor;
private VotingBoothUI ui;
private StorageManager storageManager;
private BallotQuestion[] questionsForChoosingChannel;
private BallotQuestion[] questions;
private List<BallotQuestion> questionsForChoosingChannel;
private List<BallotQuestion> questions;
private QuestionSelector questionSelector;
private LinkedBlockingQueue<ControllerCommand> queue;
@ -33,38 +33,18 @@ public class VotingBoothImpl implements VotingBoothController, Runnable {
private Logger logger;
private ControllerState state;
private boolean shutDownHasBeenCalled;
private volatile boolean shutDownHasBeenCalled;
protected final int MAX_REQUEST_IDENTIFIER = 100000;
private static int requestCounter = 0;
private UIElement waitForCommitMessage;
private UIElement waitForAuditMessage;
private UIElement waitForCastMessage;
public VotingBoothImpl () {
logger = LoggerFactory.getLogger(VotingBoothImpl.class);
logger.info("A VotingBoothImpl is constructed");
shutDownHasBeenCalled = false;
queue = new LinkedBlockingQueue<>();
waitForCommitMessage = UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while committing to ballot"))
.build();
waitForAuditMessage = UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while auditing your ballot"))
.build();
waitForCastMessage = UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while finalizing your ballot for voting"))
.build();
state = new VotingBoothImpl.ControllerState();
state = new ControllerState();
}
@Override
@ -80,42 +60,22 @@ public class VotingBoothImpl implements VotingBoothController, Runnable {
}
@Override
public void setBallotChannelChoiceQuestions(BallotQuestion[] questions) {
public void setBallotChannelChoiceQuestions(List<BallotQuestion> questions) {
logger.info("setting questions");
this.questionsForChoosingChannel = questions;
}
@Override
public void setBallotChannelChoiceQuestions(ArrayList<BallotQuestion> questions) {
BallotQuestion[] bqArr = new BallotQuestion[questions.size()];
for (int i = 0; i < bqArr.length; ++i) {
bqArr[i] = questions.get(i);
}
setBallotChannelChoiceQuestions(bqArr);
}
@Override
public void setChannelQuestionSelector(QuestionSelector selector) {
this.questionSelector = selector;
}
@Override
public void setBallotRaceQuestions(BallotQuestion[] questions) {
public void setBallotRaceQuestions(List<BallotQuestion> questions) {
logger.info("setting questions");
this.questions = questions;
}
@Override
public void setBallotRaceQuestions(ArrayList<BallotQuestion> questions) {
// TODO: why does the toArray method not work??
//BallotQuestion[] bqArr = (BallotQuestion[])(questions.toArray());
BallotQuestion[] bqArr = new BallotQuestion[questions.size()];
for (int i = 0; i < bqArr.length; ++i) {
bqArr[i] = questions.get(i);
}
setBallotRaceQuestions(bqArr);
}
@Override
public void run() {
logger.info("run command has been called");
@ -124,17 +84,6 @@ public class VotingBoothImpl implements VotingBoothController, Runnable {
}
@Override
public void shutDown() {
logger.info("shutDown command has been called");
synchronized (this) {
shutDownHasBeenCalled = true;
}
}
private void runVotingFlow () {
logger.info("entered the voting flow");
@ -151,64 +100,70 @@ public class VotingBoothImpl implements VotingBoothController, Runnable {
}
}
private void doShutDown () {
logger.info("running shutDown");
@Override
public void callShutDown() {
logger.info("callShutDown command has been called");
shutDownHasBeenCalled = true;
queue.clear();
queue.add(new ShutDownCommand());
}
private void handleSingleTask (ControllerCommand task) {
if (task instanceof RestartVotingCommand) {
doRestartVoting ();
return;
}
if (task.getBallotSerialNumber() != state.currentBallotSerialNumber) {
if (task.getBallotSerialNumber() != state.currentBallotSerialNumber && !(task instanceof RestartVotingCommand)) {
// probably an old command relating to some old ballot serial number. Simply log it and ignore it.
String errorMessage = "handleSingleTask: received a task too old. " +
task.getBallotSerialNumber() + " " + state.currentBallotSerialNumber;
logger.warn (errorMessage);
logger.debug(errorMessage);
return;
}
if (task instanceof CastCommand) {
doCast();
return;
if (task instanceof RestartVotingCommand) {
doRestartVoting ();
}
else if (task instanceof CastCommand) {
doFinalize(false);
}
else if (task instanceof AuditCommand) {
doAudit();
return;
doFinalize(true);
}
else if (task instanceof ChannelChoiceCommand) {
doChooseChannel();
return;
}
else if (task instanceof ChannelDeterminedCommand) {
doSetChannelAndAskQuestions ((ChannelDeterminedCommand)task);
return;
}
else if (task instanceof EncryptAndCommitBallotCommand) {
doCommit ((EncryptAndCommitBallotCommand)task);
return;
}
else if (task instanceof ReportErrorCommand) {
doReportErrorAndForceRestart((ReportErrorCommand)task);
}
else if (task instanceof ShutDownCommand) {
// wasShutDownCalled is now true. Do nothing.
// The program will not access the command queue anymore, and will exit normally
if (!wasShutDownCalled()) {
logger.warn("Received a ShutDownCommand command. At this point shutDownHasBeenCalled flag should have been True, but it's not...");
shutDownHasBeenCalled = true;
}
}
else {
String errorMessage = "handleSingleTask: unknown type of ControllerCommand received: " +
task.getClass().getName();
logger.error(errorMessage);
throw new RuntimeException(errorMessage);
logger.error("handleSingleTask: unknown type of ControllerCommand received: " + task.getClass().getName());
doReportErrorAndForceRestart(SystemMessages.getSomethingWrongMessage());
}
}
/*
Map<string, funcImpl> taskHandlers = new Map<>() {
CaskTask.getClass().getName() : doCast(),
ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
}
*/
private synchronized boolean wasShutDownCalled () {
private boolean wasShutDownCalled () {
return shutDownHasBeenCalled;
}
private void doShutDown () {
logger.info("running callShutDown");
state.clearPlaintext();
state.clearCiphertext();
state.stateIdentifier = VBState.SHUT_DOWN;
//TODO: add commands to actually shut down the machine
}
private void doRestartVoting () {
queue.clear();
state.clearPlaintext();
@ -216,7 +171,22 @@ ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
state.stateIdentifier = VBState.NEW_VOTER;
state.currentBallotSerialNumber += 1;
ui.startNewVoterSession(new NewVoterCallback(generateRequestIdentifier(), state.currentBallotSerialNumber , this.queue));
ui.startNewVoterSession(new NewVoterCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
private void doReportErrorAndForceRestart(ReportErrorCommand task) {
doReportErrorAndForceRestart(task.getErrorMessage());
}
private void doReportErrorAndForceRestart(UIElement errorMessage) {
queue.clear();
state.clearPlaintext();
state.clearCiphertext();
state.stateIdentifier = VBState.FATAL_ERROR_FORCE_NEW_VOTER;
state.currentBallotSerialNumber += 1;
ui.showErrorMessageWithButtons(errorMessage,
new UIElement[]{SystemMessages.getRestartVotingButton()},
new ErrorMessageRestartCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
private void doChooseChannel () {
@ -227,7 +197,7 @@ ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
new ChannelChoiceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
else {
logger.warn("doChooseChannel: current state is " + state.stateIdentifier);
logger.debug("doChooseChannel: current state is " + state.stateIdentifier);
// ignore this request
}
}
@ -236,15 +206,14 @@ ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
if (state.stateIdentifier == VBState.CHOOSE_CHANNEL) {
logger.debug("doing set channel and ask questions");
state.stateIdentifier = VBState.ANSWER_QUESTIONS;
BallotAnswer[] channelChoiceAnswers = task.channelChoiceAnswers;
List<BallotAnswer> channelChoiceAnswers = task.channelChoiceAnswers;
state.channelIdentifier = questionSelector.getChannelIdentifier(channelChoiceAnswers);
state.channelSpecificQuestions = questionSelector.selectQuestionsForVoter(channelChoiceAnswers);
state.stateIdentifier = VBState.ANSWER_QUESTIONS;
ui.askVoterQuestions(state.channelSpecificQuestions,
new VotingCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
else {
logger.warn("doSetChannelAndAskQuestions: current state is " + state.stateIdentifier);
logger.debug("doSetChannelAndAskQuestions: current state is " + state.stateIdentifier);
// ignore this request
}
}
@ -254,10 +223,8 @@ ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
if (state.stateIdentifier == VBState.ANSWER_QUESTIONS) {
logger.debug("doing commit");
state.stateIdentifier = VBState.COMMITTING_TO_BALLOT;
VotingBoothEncryptor.EncryptionAndSecrets encryptionAndSecrets = encryptor.encrypt(state.plaintextBallot);
state.encryptedBallot = encryptionAndSecrets.getEncryptedBallot();
state.secrets = encryptionAndSecrets.getSecrets();
ui.notifyVoterToWaitForFinish(waitForCommitMessage,
setBallotData (task);
ui.notifyVoterToWaitForFinish(SystemMessages.getWaitForCommitMessage(),
new WaitForFinishCallback(generateRequestIdentifier(),
state.currentBallotSerialNumber,
this.queue));
@ -266,38 +233,38 @@ ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
new OutputDeviceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
else {
logger.warn("doCommit: current state is " + state.stateIdentifier);
logger.debug("doCommit: current state is " + state.stateIdentifier);
// ignore this request
}
}
private void doAudit () {
if (state.stateIdentifier == VBState.CAST_OR_AUDIT) {
logger.debug("doing audit");
state.stateIdentifier = VBState.FINALIZING;
outputDevice.audit(state.secrets,
new OutputDeviceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
ui.notifyVoterToWaitForFinish(waitForAuditMessage,
new WaitForFinishCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
else {
logger.warn("doAudit: current state is " + state.stateIdentifier);
// ignore this request
}
private void setBallotData (EncryptAndCommitBallotCommand task) {
state.plaintextBallot = PlaintextBallot.newBuilder()
.setSerialNumber(task.getBallotSerialNumber())
.addAllAnswers(task.getVotingAnswers())
.build();
VotingBoothEncryptor.EncryptionAndSecrets encryptionAndSecrets = encryptor.encrypt(state.plaintextBallot);
state.encryptedBallot = encryptionAndSecrets.getEncryptedBallot();
state.secrets = encryptionAndSecrets.getSecrets();
}
private void doCast () {
private void doFinalize (boolean auditRequested) {
if (state.stateIdentifier == VBState.CAST_OR_AUDIT) {
logger.debug("casting ballot");
logger.debug("finalizing");
state.stateIdentifier = VBState.FINALIZING;
outputDevice.castBallot(
new OutputDeviceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
ui.notifyVoterToWaitForFinish(waitForCastMessage,
ui.notifyVoterToWaitForFinish(SystemMessages.getWaitForAuditMessage(),
new WaitForFinishCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
if (auditRequested) {
outputDevice.audit(state.secrets,
new OutputDeviceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
else {
outputDevice.castBallot(
new OutputDeviceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
}
else {
logger.warn("doCast: current state is " + state.stateIdentifier);
logger.debug("doFinalize: current state is " + state.stateIdentifier);
// ignore this request
}
}
@ -312,14 +279,16 @@ ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
ANSWER_QUESTIONS,
COMMITTING_TO_BALLOT,
CAST_OR_AUDIT,
FINALIZING
FINALIZING,
FATAL_ERROR_FORCE_NEW_VOTER,
SHUT_DOWN
}
private class ControllerState {
public VotingBoothImpl.VBState stateIdentifier;
public VBState stateIdentifier;
public int channelIdentifier;
public BallotQuestion[] channelSpecificQuestions;
public List<BallotQuestion> channelSpecificQuestions;
public PlaintextBallot plaintextBallot;
public EncryptedBallot encryptedBallot;
public BallotSecrets secrets;
@ -338,12 +307,10 @@ ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
public void clearPlaintext () {
//TODO: Do we need safe erasure?
plaintextBallot = null;
}
public void clearCiphertext () {
//TODO: Do we need safe erasure?
encryptedBallot = null;
secrets = null;
}

View File

@ -1,6 +1,6 @@
package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.SystemMessages;
import meerkat.voting.controller.commands.*;
import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.RestartVotingCommand;
@ -10,7 +10,7 @@ import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
public class CastOrAuditCallback extends ControllerCallback {
public class CastOrAuditCallback extends ControllerCallback<FinalizeBallotChoice> {
protected final static Logger logger = LoggerFactory.getLogger(CastOrAuditCallback.class);
public CastOrAuditCallback(int requestId,
@ -20,18 +20,18 @@ public class CastOrAuditCallback extends ControllerCallback {
}
@Override
public void onSuccess(Object result) {
assert (result instanceof FinalizeBallotChoice);
public void onSuccess(FinalizeBallotChoice result) {
if (result == FinalizeBallotChoice.CAST) {
controllerQueue.add(new CastCommand(getRequestIdentifier(), getBallotSerialNumber()));
enqueueCommand(new CastCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
else if (result == FinalizeBallotChoice.AUDIT) {
controllerQueue.add(new AuditCommand(getRequestIdentifier(), getBallotSerialNumber()));
enqueueCommand(new AuditCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
else {
logger.error("CastOrAuditCallback got an unrecognized response: " + result);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
SystemMessages.getUnrecognizedFinalizeResponseMessage()));
}
}
@ -39,7 +39,9 @@ public class CastOrAuditCallback extends ControllerCallback {
@Override
public void onFailure(Throwable t) {
logger.error("CastOrAuditCallback got a failure: " + t);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
SystemMessages.getUnrecognizedFinalizeResponseMessage()));
}
}

View File

@ -1,18 +1,18 @@
package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting;
import meerkat.voting.controller.commands.ChannelDeterminedCommand;
import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.RestartVotingCommand;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.SystemMessages;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Created by hai on 11/04/16.
*/
public class ChannelChoiceCallback extends ControllerCallback {
public class ChannelChoiceCallback extends ControllerCallback<List<BallotAnswer>> {
protected final static Logger logger = LoggerFactory.getLogger(ChannelChoiceCallback.class);
public ChannelChoiceCallback(int requestId,
@ -22,22 +22,22 @@ public class ChannelChoiceCallback extends ControllerCallback {
}
@Override
public void onSuccess(Object result) {
assert (result instanceof Voting.BallotAnswer[]);
if (((Voting.BallotAnswer[])result).length != 0) {
public void onSuccess(List<BallotAnswer> result) {
if (result.size() != 0) {
logger.debug("callback for channel choice returned success");
controllerQueue.add(
new ChannelDeterminedCommand(getRequestIdentifier(), getBallotSerialNumber(), (Voting.BallotAnswer[]) result));
enqueueCommand(new ChannelDeterminedCommand(getRequestIdentifier(), getBallotSerialNumber(), result));
}
else {
logger.debug("ChannelChoiceCallback got a cancellation response: " + result);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
}
@Override
public void onFailure(Throwable t) {
logger.error("channel choice initiated a failure: " + t);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
SystemMessages.getUnsuccessfulChannelChoiceMessage()));
}
}

View File

@ -1,14 +1,18 @@
package meerkat.voting.controller.callbacks;
/**
* Created by hai on 18/05/16.
*/
import com.google.common.util.concurrent.FutureCallback;
import meerkat.voting.controller.commands.ControllerCommand;
import java.util.concurrent.LinkedBlockingQueue;
public abstract class ControllerCallback implements FutureCallback {
public abstract class ControllerCallback<T> implements FutureCallback<T> {
private final int requestIdentifier;
private final long ballotSerialNumber;
protected LinkedBlockingQueue<ControllerCommand> controllerQueue;
private LinkedBlockingQueue<ControllerCommand> controllerQueue;
protected ControllerCallback (int requestId,
long ballotSerialNumber,
@ -21,7 +25,13 @@ public abstract class ControllerCallback implements FutureCallback {
protected int getRequestIdentifier () {
return requestIdentifier;
}
protected long getBallotSerialNumber () {
return ballotSerialNumber;
}
protected void enqueueCommand (ControllerCommand command) {
controllerQueue.add(command);
}
}

View File

@ -0,0 +1,29 @@
package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.RestartVotingCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
public class ErrorMessageRestartCallback extends ControllerCallback<Integer> {
protected final static Logger logger = LoggerFactory.getLogger(ErrorMessageRestartCallback.class);
public ErrorMessageRestartCallback(int requestId,
long ballotSerialNumber,
LinkedBlockingQueue<ControllerCommand> controllerQueue) {
super(requestId, ballotSerialNumber, controllerQueue);
}
@Override
public void onSuccess(Integer result) {
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
@Override
public void onFailure(Throwable t) {
logger.error("Error message execution initiated a failure: " + t);
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
}

View File

@ -1,32 +0,0 @@
package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.commands.ControllerCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Created by hai on 21/04/16.
*/
public class HaltCallback extends ControllerCallback{
protected final static Logger logger = LoggerFactory.getLogger(HaltCallback.class);
public HaltCallback(int requestId,
long ballotSerialNumber,
LinkedBlockingQueue<ControllerCommand> controllerQueue) {
super(requestId, ballotSerialNumber, controllerQueue);
}
@Override
public void onSuccess(Object result) {
throw new UnsupportedOperationException("HaltCallback: onSuccess: There cannot be a successful return for this task. Returned value is " + result);
}
@Override
public void onFailure(Throwable t) {
logger.error("Halting initiated a failure: " + t);
}
}

View File

@ -8,7 +8,7 @@ import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
public class NewVoterCallback extends ControllerCallback {
public class NewVoterCallback extends ControllerCallback<Void> {
protected final static Logger logger = LoggerFactory.getLogger(NewVoterCallback.class);
public NewVoterCallback(int requestId,
@ -18,15 +18,14 @@ public class NewVoterCallback extends ControllerCallback {
}
@Override
public void onSuccess(Object v) {
public void onSuccess(Void v) {
logger.debug("callback for new voting returned success");
assert v==null;
controllerQueue.add(new ChannelChoiceCommand(getRequestIdentifier(), getBallotSerialNumber()));
enqueueCommand(new ChannelChoiceCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
@Override
public void onFailure(Throwable t) {
logger.error("New voting session got a failure: " + t);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
}

View File

@ -1,13 +1,15 @@
package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.SystemMessages;
import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.ReportErrorCommand;
import meerkat.voting.controller.commands.RestartVotingCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
public class OutputDeviceCallback extends ControllerCallback {
public class OutputDeviceCallback extends ControllerCallback<Void> {
protected final static Logger logger = LoggerFactory.getLogger(OutputDeviceCallback.class);
public OutputDeviceCallback(int requestId,
@ -17,13 +19,14 @@ public class OutputDeviceCallback extends ControllerCallback {
}
@Override
public void onSuccess(Object v) {
assert v instanceof Void;
public void onSuccess(Void v) {
}
@Override
public void onFailure(Throwable t) {
logger.error("WaitForFinishCallback got a failure: " + t);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
SystemMessages.getOutputDeviceFailureMessage()));
}
}

View File

@ -1,15 +1,18 @@
package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.SystemMessages;
import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.EncryptAndCommitBallotCommand;
import meerkat.voting.controller.commands.ReportErrorCommand;
import meerkat.voting.controller.commands.RestartVotingCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
public class VotingCallback extends ControllerCallback {
public class VotingCallback extends ControllerCallback<List<BallotAnswer>> {
protected final static Logger logger = LoggerFactory.getLogger(VotingCallback.class);
public VotingCallback(int requestId,
@ -19,22 +22,22 @@ public class VotingCallback extends ControllerCallback {
}
@Override
public void onSuccess(Object result) {
assert result instanceof Voting.BallotAnswer[];
if (((Voting.BallotAnswer[])result).length != 0) {
public void onSuccess(List<BallotAnswer> result) {
if (result.size() != 0) {
logger.debug("callback for voting returned success");
controllerQueue.add(
new EncryptAndCommitBallotCommand(getRequestIdentifier(), getBallotSerialNumber(), (Voting.BallotAnswer[]) result));
enqueueCommand(new EncryptAndCommitBallotCommand(getRequestIdentifier(), getBallotSerialNumber(), result));
}
else {
logger.debug("VotingCallback got a cancellation response: " + result);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
}
@Override
public void onFailure(Throwable t) {
logger.error("voting initiated a failure: " + t);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
SystemMessages.getUnsuccessfulVotingMessage()));
}
}

View File

@ -1,13 +1,15 @@
package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.SystemMessages;
import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.ReportErrorCommand;
import meerkat.voting.controller.commands.RestartVotingCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
public class WaitForFinishCallback extends ControllerCallback {
public class WaitForFinishCallback extends ControllerCallback<Void> {
protected final static Logger logger = LoggerFactory.getLogger(WaitForFinishCallback.class);
public WaitForFinishCallback(int requestId,
@ -17,13 +19,14 @@ public class WaitForFinishCallback extends ControllerCallback {
}
@Override
public void onSuccess(Object v) {
assert v instanceof Void;
public void onSuccess(Void v) {
}
@Override
public void onFailure(Throwable t) {
logger.error("WaitForFinishCallback got a failure: " + t);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
SystemMessages.getSomethingWrongMessage()));
}
}

View File

@ -1,14 +1,15 @@
package meerkat.voting.controller.commands;
import meerkat.protobuf.Voting;
import meerkat.protobuf.Voting.*;
import java.util.List;
/**
* Created by hai on 11/04/16.
*/
public class ChannelDeterminedCommand extends ControllerCommand {
public Voting.BallotAnswer[] channelChoiceAnswers;
public List<BallotAnswer> channelChoiceAnswers;
public ChannelDeterminedCommand(int requestIdentifier, long ballotSerialNumber, Voting.BallotAnswer[] answers) {
public ChannelDeterminedCommand(int requestIdentifier, long ballotSerialNumber, List<BallotAnswer> answers) {
super(requestIdentifier, ballotSerialNumber);
channelChoiceAnswers = answers;
}

View File

@ -1,12 +1,18 @@
package meerkat.voting.controller.commands;
import meerkat.protobuf.Voting;
import meerkat.protobuf.Voting.*;
import java.util.List;
public class EncryptAndCommitBallotCommand extends ControllerCommand {
public Voting.BallotAnswer[] votingAnswers;
private final List<BallotAnswer> votingAnswers;
public EncryptAndCommitBallotCommand(int requestIdentifier, long ballotSerialNumber, Voting.BallotAnswer[] answers) {
public EncryptAndCommitBallotCommand(int requestIdentifier, long ballotSerialNumber, List<BallotAnswer> answers) {
super(requestIdentifier, ballotSerialNumber);
votingAnswers = answers;
}
public List<BallotAnswer> getVotingAnswers() {
return votingAnswers;
}
}

View File

@ -0,0 +1,16 @@
package meerkat.voting.controller.commands;
import meerkat.protobuf.Voting.*;
public class ReportErrorCommand extends ControllerCommand {
private final UIElement errorMessage;
public ReportErrorCommand(int requestIdentifier, long ballotSerialNumber, UIElement errorMessage) {
super(requestIdentifier, ballotSerialNumber);
this.errorMessage = errorMessage;
}
public UIElement getErrorMessage() {
return errorMessage;
}
}

View File

@ -0,0 +1,10 @@
package meerkat.voting.controller.commands;
/**
* Created by hai on 11/04/16.
*/
public class ShutDownCommand extends ControllerCommand {
public ShutDownCommand() {
super(0, 0);
}
}

View File

@ -1,20 +1,18 @@
package meerkat.voting.controller.selector;
import meerkat.protobuf.Voting.*;
import java.util.List;
/**
* Created by hai on 02/05/16.
*/
public abstract class QuestionSelector {
public interface QuestionSelector {
protected Object selectionData;
protected BallotQuestion[] allBallotQuestions;
public void setAllBallotQuestions (List<BallotQuestion> allBallotQuestions);
public QuestionSelector(Object selectionData, BallotQuestion[] allBallotQuestions) {
this.selectionData = selectionData;
this.allBallotQuestions = allBallotQuestions;
}
public void setSelectionData(SelectionData data);
public abstract int getChannelIdentifier (BallotAnswer[] channelChoiceAnswers);
public abstract BallotQuestion[] selectQuestionsForVoter (BallotAnswer[] channelChoiceAnswers);
public int getChannelIdentifier (List<BallotAnswer> channelChoiceAnswers);
public List<BallotQuestion> selectQuestionsForVoter (List<BallotAnswer> channelChoiceAnswers);
}

View File

@ -0,0 +1,7 @@
package meerkat.voting.controller.selector;
/**
* Created by hai on 11/05/16.
*/
public class SelectionData {
}

View File

@ -1,39 +1,60 @@
package meerkat.voting.controller.selector;
import java.util.HashMap;
import java.lang.Math;
/**
* Created by hai on 02/05/16.
* This class is the SelectionData used to initialize a SimpleListCategoriesSelector.
* It holds a table of question categories.
* Every answer in the channel choice phase dictates a category of questions to include in the ballot.
* This class is simply only the data class of these categories.
* Specifically the questionSelectionsByAnswer member is this categories table:
* questionSelectionsByAnswer[questionNumber][answerNumber] is an array of question indices to include in ballot
* whenever the voter answered answerNumber on question questionNumber in the channel choice phase.
* Also the sharedDefaults member is a category of question indices to include for EVERY voter,
* regardless of his channel choice answers
*/
public class SimpleCategoriesData {
public class SimpleCategoriesData extends SelectionData{
private int[] sharedDefaults;
private HashMap<SingleChosenAnswer, int[]> questionsSelections;
private int[] sharedDefaults; // a category of questions to include for every voter
// the categories
// first index is the question number, the second is the answer number. the value is an array of question indices in this category
private int[][][] questionSelectionsByAnswer;
private int maxQuestionNumber;
public SimpleCategoriesData() {
sharedDefaults = new int[0];
questionsSelections = new HashMap();
questionSelectionsByAnswer = new int[0][][];
maxQuestionNumber = -1;
}
public void setSharedDefaults(int[] sharedDefaultsIndices) {
this.sharedDefaults = sharedDefaultsIndices;
maxQuestionNumber = Math.max(maxQuestionNumber, maxInt(sharedDefaultsIndices));
this.sharedDefaults = sharedDefaultsIndices.clone();
maxQuestionNumber = Math.max(maxQuestionNumber, maxIntInArray(sharedDefaultsIndices));
}
public int[] getSharedDefaults() {
return sharedDefaults;
}
public void setItem(SingleChosenAnswer key, int[] questionIndices) {
questionsSelections.put(key, questionIndices);
maxQuestionNumber = Math.max(maxQuestionNumber, maxInt(questionIndices));
public void setItem(int questionNumber, int answerNumber, int[] questionIndices) {
if (questionNumber >= questionSelectionsByAnswer.length) {
int[][][] tmp = new int[questionNumber+1][][];
System.arraycopy(questionSelectionsByAnswer, 0, tmp, 0, questionSelectionsByAnswer.length);
tmp[questionNumber] = new int[0][];
questionSelectionsByAnswer = tmp;
}
if (answerNumber >= questionSelectionsByAnswer[questionNumber].length) {
int[][] tmp = new int[answerNumber+1][];
System.arraycopy(questionSelectionsByAnswer[questionNumber], 0,
tmp, 0, questionSelectionsByAnswer[questionNumber].length);
questionSelectionsByAnswer[questionNumber] = tmp;
}
questionSelectionsByAnswer[questionNumber][answerNumber] = questionIndices.clone();
maxQuestionNumber = Math.max(maxQuestionNumber, maxIntInArray(questionIndices));
}
public int[] getItem(SingleChosenAnswer key) {
return questionsSelections.get(key);
public int[] getItem(int questionNumber, int answerNumber) {
return questionSelectionsByAnswer[questionNumber][answerNumber];
}
public int getMaxQuestionNumber() {
@ -41,7 +62,7 @@ public class SimpleCategoriesData {
}
private static int maxInt(int[] arr) {
private static int maxIntInArray(int[] arr) {
int m = -1;
for (int i: arr) {
m = Math.max(i , m);
@ -53,8 +74,11 @@ public class SimpleCategoriesData {
String s = "SimpleCategoriesData:\n";
s += "shared : " + arrayToString(sharedDefaults) + "\n";
s += "map : \n";
for (SingleChosenAnswer key : questionsSelections.keySet()) {
s += key + " : " + arrayToString(questionsSelections.get(key)) + "\n";
for (int questionNumber = 0; questionNumber < questionSelectionsByAnswer.length; ++questionNumber) {
for (int answerNumber = 0; answerNumber < questionSelectionsByAnswer[questionNumber].length; ++answerNumber) {
s += "(" + questionNumber + ", " + answerNumber + ") -> " +
arrayToString(questionSelectionsByAnswer[questionNumber][answerNumber]) + "\n";
}
}
return s;
}

View File

@ -1,75 +1,88 @@
package meerkat.voting.controller.selector;
import jdk.nashorn.internal.runtime.regexp.joni.exception.ValueException;
import meerkat.protobuf.Voting.*;
import meerkat.protobuf.Voting.BallotAnswer;
import meerkat.protobuf.Voting.BallotQuestion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/**
* Created by hai on 02/05/16.
* A simple implementation of a QuestionSelector.
* This implementation simply regards every single answer in the channel choice phase as an identifier of a category
* Every category is an array of ballot questions.
* Data of categories is initialized and stored by a SimpleCategoriesData object.
* After receiving the answers from a channel choice phase, this class simply gathers all the categories
* chosen and compiles the list of ballot questions to include in the ballot for this voter (a question
* is included if its index appears in any chosen category, or in the default category shared by all voters)
*/
public class SimpleListCategoriesSelector extends QuestionSelector {
public class SimpleListCategoriesSelector implements QuestionSelector {
protected final static Logger logger = LoggerFactory.getLogger(SimpleListCategoriesSelector.class);
private BallotQuestion[] allBallotQuestions;
private SimpleCategoriesData selectionData;
private boolean[] isSelected;
public SimpleListCategoriesSelector(Object selectionData, BallotQuestion[] allBallotQuestions) {
super(selectionData, allBallotQuestions);
initializeAttributes (allBallotQuestions);
}
public SimpleListCategoriesSelector(Object selectionData, ArrayList<BallotQuestion> allBallotQuestions) {
super(selectionData, null);
BallotQuestion[] questionsArr = new BallotQuestion[allBallotQuestions.size()];
for (int i = 0; i < questionsArr.length; ++i) {
questionsArr[i] = allBallotQuestions.get(i);
}
initializeAttributes (questionsArr);
public SimpleListCategoriesSelector() {
this.allBallotQuestions = null;
this.selectionData = null;
}
private void initializeAttributes (BallotQuestion[] allBallotQuestions) {
assert selectionData instanceof SimpleCategoriesData;
this.allBallotQuestions = allBallotQuestions;
SimpleCategoriesData data = (SimpleCategoriesData) selectionData;
int maxQuestionNumber = data.getMaxQuestionNumber();
int length = allBallotQuestions.length;
if (maxQuestionNumber >= length) {
String errorMessage = "Selection data refers to question index " + maxQuestionNumber + " while we have only " + length + " questions totally";
logger.error(errorMessage);
throw new ValueException(errorMessage);
}
isSelected = new boolean[allBallotQuestions.length];
}
@Override
public int getChannelIdentifier(BallotAnswer[] channelChoiceAnswers) {
public void setAllBallotQuestions(List<BallotQuestion> allBallotQuestions) {
this.allBallotQuestions = (BallotQuestion[])allBallotQuestions.toArray();
isSelected = new boolean[this.allBallotQuestions.length];
assertDataValid();
}
@Override
public void setSelectionData(SelectionData data) {
if (! (data instanceof SimpleCategoriesData)) {
String errorMessage = "Initialization of SimpleListCategoriesSelector with wrong object type";
logger.error(errorMessage);
throw new RuntimeException(errorMessage);
}
this.selectionData = (SimpleCategoriesData)data;
assertDataValid();
}
private void assertDataValid () {
if (selectionData != null && allBallotQuestions != null) {
int questionsLength = allBallotQuestions.length;
int maxQuestionNumber = selectionData.getMaxQuestionNumber();
if (maxQuestionNumber >= questionsLength) {
String errorMessage = "Selection data refers to question index " + maxQuestionNumber + " while we have only " + questionsLength + " questions totally";
logger.error(errorMessage);
throw new IndexOutOfBoundsException(errorMessage);
}
}
}
@Override
public int getChannelIdentifier(List<BallotAnswer> channelChoiceAnswers) {
return 0;
}
@Override
public BallotQuestion[] selectQuestionsForVoter(BallotAnswer[] channelChoiceAnswers) {
SimpleCategoriesData data = (SimpleCategoriesData)selectionData;
public List<BallotQuestion> selectQuestionsForVoter(List<BallotAnswer> channelChoiceAnswers) {
java.util.Arrays.fill(isSelected, false);
markSelected(data.getSharedDefaults());
markSelectionsOfVoterChannelChoice (channelChoiceAnswers);
markSelected(selectionData.getSharedDefaults());
markSelectionsOfVoterChannelChoice ((BallotAnswer[])channelChoiceAnswers.toArray());
int[] questionIndices = extractSelectedIndices();
BallotQuestion[] selectedQuestions = new BallotQuestion[questionIndices.length];
for (int i = 0; i < questionIndices.length; ++i) {
selectedQuestions[i] = allBallotQuestions[questionIndices[i]];
List<BallotQuestion> selectedQuestions = new ArrayList<>();
for (int questionIndex: questionIndices) {
selectedQuestions.add(allBallotQuestions[questionIndex]);
}
return selectedQuestions;
}
private void markSelectionsOfVoterChannelChoice(BallotAnswer[] channelChoiceAnswers) {
SimpleCategoriesData data = (SimpleCategoriesData)selectionData;
for (int q = 0; q < channelChoiceAnswers.length; ++q) {
BallotAnswer ballotAnswer = channelChoiceAnswers[q];
assertAnswerLengthIsOne(ballotAnswer, q);
SingleChosenAnswer key = new SingleChosenAnswer(q, ballotAnswer.getAnswer(0));
assertKeyInSelectionData(key);
markSelected(data.getItem(key));
assertKeyInSelectionData(q, (int)ballotAnswer.getAnswer(0));
markSelected(selectionData.getItem(q, (int)ballotAnswer.getAnswer(0)));
}
}
@ -81,17 +94,16 @@ public class SimpleListCategoriesSelector extends QuestionSelector {
errorMessage += " " + i;
}
logger.error(errorMessage);
throw new ValueException(errorMessage);
throw new IllegalArgumentException(errorMessage);
}
}
private void assertKeyInSelectionData(SingleChosenAnswer key) {
SimpleCategoriesData data = (SimpleCategoriesData)selectionData;
int[] questionListToAdd = data.getItem(key);
private void assertKeyInSelectionData(int questionNumber, int answerNumber) {
int[] questionListToAdd = selectionData.getItem(questionNumber, answerNumber);
if (null == questionListToAdd) {
String errorMessage = "SimpleListCategoriesSelector could not resolve answer number " + key.answerNumber + " for question number " + key.questionNumber;
String errorMessage = "SimpleListCategoriesSelector could not resolve answer number " + answerNumber + " for question number " + questionNumber;
logger.error(errorMessage);
throw new ValueException(errorMessage);
throw new IllegalArgumentException(errorMessage);
}
}

View File

@ -1,31 +0,0 @@
package meerkat.voting.controller.selector;
/**
* Created by hai on 02/05/16.
*/
public class SingleChosenAnswer {
public int questionNumber;
public long answerNumber;
public SingleChosenAnswer(int questionNumber, long answerNumber) {
this.questionNumber = questionNumber;
this.answerNumber = answerNumber;
}
public String toString() {
return "SingleChosenAnswer(" + questionNumber + ", " + answerNumber + ")";
}
public boolean equals (Object other) {
if (!(other instanceof SingleChosenAnswer)) {
return false;
}
SingleChosenAnswer o = (SingleChosenAnswer)other;
return (o.questionNumber == this.questionNumber) && (o.answerNumber == this.answerNumber);
}
@Override
public int hashCode () {
return questionNumber*1000 + (int)answerNumber;
}
}

View File

@ -7,9 +7,7 @@ import meerkat.protobuf.Voting.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import meerkat.voting.controller.callbacks.*;
@ -45,54 +43,47 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
logger.info("entered the voting flow");
while (true) {
try {
UICommand task = queue.take();
handleSingleTask (task);
UICommand command = queue.take();
handleSingleCommand(command);
}
catch (InterruptedException e) {
logger.warn ("Interrupted while reading from task queue " + e);
logger.warn ("Interrupted while reading from command queue " + e);
}
}
}
private void handleSingleTask (UICommand task) {
if (!(task instanceof TickCommand)) {
private void handleSingleCommand(UICommand command) {
if (!(command instanceof TickCommand)) {
if (startWaitingTime != null) {
stopWaiting();
}
}
if (task instanceof StartSessionUICommand) {
doShowWelcomeScreen((StartSessionUICommand)task);
return;
if (command instanceof StartSessionUICommand) {
doShowWelcomeScreen((StartSessionUICommand)command);
}
else if (task instanceof ChannelChoiceUICommand) {
doAskChannelChoiceQuestions((ChannelChoiceUICommand)task);
return;
else if (command instanceof ChannelChoiceUICommand) {
doAskChannelChoiceQuestions((ChannelChoiceUICommand)command);
}
else if (task instanceof RaceVotingUICommand) {
doAskVotingQuestions((RaceVotingUICommand)task);
return;
else if (command instanceof RaceVotingUICommand) {
doAskVotingQuestions((RaceVotingUICommand)command);
}
else if (task instanceof CastOrAuditUICommand) {
doCastOrAudit ((CastOrAuditUICommand)task);
return;
else if (command instanceof CastOrAuditUICommand) {
doCastOrAudit ((CastOrAuditUICommand)command);
}
else if (task instanceof HaltCommand) {
doHalt((HaltCommand)task);
return;
else if (command instanceof FatalErrorUICommand) {
doFatalError((FatalErrorUICommand)command);
}
else if (task instanceof WaitForFinishCommand) {
doWaitForFinish((WaitForFinishCommand)task);
return;
else if (command instanceof WaitForFinishUICommand) {
doWaitForFinish((WaitForFinishUICommand)command);
}
else if (task instanceof TickCommand) {
else if (command instanceof TickCommand) {
doTick ();
return;
}
else {
String errorMessage = "handleSingleTask: unknown type of UICommand received: " +
task.getClass().getName();
String errorMessage = "handleSingleCommand: unknown type of UICommand received: " +
command.getClass().getName();
logger.error(errorMessage);
throw new RuntimeException(errorMessage);
}
@ -105,13 +96,13 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
queue.add(new StartSessionUICommand((ControllerCallback)callback));
}
private void doShowWelcomeScreen(StartSessionUICommand task) {
private void doShowWelcomeScreen(StartSessionUICommand command) {
logger.debug("UI entered doShowWelcomeScreen");
System.out.println("Welcome, new voter!");
waitForEnter(null);
ControllerCallback callback = task.getCallback();
ControllerCallback callback = command.getCallback();
assert (callback instanceof NewVoterCallback);
((NewVoterCallback)callback).onSuccess(null);
callback.onSuccess(null);
}
private void stopWaiting () {
@ -138,45 +129,45 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
}
@Override
public void chooseChannel(BallotQuestion[] questions, FutureCallback<BallotAnswer[]> callback) {
public void chooseChannel(List<BallotQuestion> questions, FutureCallback<List<BallotAnswer>> callback) {
logger.debug("UI interface call to chooseChannel");
ChannelChoiceUICommand task = new ChannelChoiceUICommand(questions, (ControllerCallback)callback);
queue.add(task);
ChannelChoiceUICommand command = new ChannelChoiceUICommand(questions, (ControllerCallback)callback);
queue.add(command);
}
private void doAskChannelChoiceQuestions (ChannelChoiceUICommand task) {
private void doAskChannelChoiceQuestions (ChannelChoiceUICommand command) {
logger.debug("UI: doAskChannelChoiceQuestions");
System.out.println("Showing questions for choosing channel:\n");
try {
BallotAnswer[] answers = askVoterForAnswers(task.getQuestions());
task.getCallback().onSuccess(answers);
List<BallotAnswer> answers = askVoterForAnswers(command.getQuestions());
command.getCallback().onSuccess(answers);
}
catch (IOException e) {
String errorMessage = "Channel choice failed due to IOException: " + e;
logger.error (errorMessage);
System.err.println(errorMessage);
task.getCallback().onFailure(e);
command.getCallback().onFailure(e);
}
}
@Override
public void askVoterQuestions(BallotQuestion[] questions, FutureCallback<BallotAnswer[]> callback) {
public void askVoterQuestions(List<BallotQuestion> questions, FutureCallback<List<BallotAnswer>> callback) {
logger.debug("UI interface call to chooseChannel");
queue.add(new RaceVotingUICommand(questions, (ControllerCallback)callback));
}
private void doAskVotingQuestions (RaceVotingUICommand task) {
private void doAskVotingQuestions (RaceVotingUICommand command) {
logger.debug("UI: doAskVotingQuestions");
System.out.println("Showing questions for race voting:\n");
try {
BallotAnswer[] answers = askVoterForAnswers(task.getQuestions());
task.getCallback().onSuccess(answers);
List<BallotAnswer> answers = askVoterForAnswers(command.getQuestions());
command.getCallback().onSuccess(answers);
}
catch (IOException e) {
String errorMessage = "Asking voting questions failed due to IOException: " + e;
logger.error (errorMessage);
System.err.println(errorMessage);
task.getCallback().onFailure(e);
command.getCallback().onFailure(e);
}
}
@ -186,7 +177,7 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
queue.add(new CastOrAuditUICommand((ControllerCallback)callback));
}
private void doCastOrAudit(CastOrAuditUICommand task) {
private void doCastOrAudit(CastOrAuditUICommand command) {
logger.debug("UI entered doCastOrAudit");
System.out.println ("Finalizing your vote. Do you wish to (C)ast or (A)udit?");
@ -203,30 +194,30 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
else {
throw new IllegalArgumentException("UI could not understand the answer for cast/audit question '" + s + "'");
}
ControllerCallback callback = task.getCallback();
ControllerCallback callback = command.getCallback();
assert (callback instanceof CastOrAuditCallback);
((CastOrAuditCallback)callback).onSuccess(fChoice);
}
catch (IllegalArgumentException|IOException e) {
String errorMessage = "doCastOrAudit: some error with reading input from console. details: " + e;
logger.error(errorMessage);
task.getCallback().onFailure(e);
command.getCallback().onFailure(e);
}
}
@Override
public void notifyVoterToWaitForFinish(UIElement message, FutureCallback<Boolean> callback) {
public void notifyVoterToWaitForFinish(UIElement message, FutureCallback<Void> callback) {
logger.debug("UI interface call to notifyVoterToWaitForFinish");
queue.add(new WaitForFinishCommand(message, (ControllerCallback)callback));
queue.add(new WaitForFinishUICommand(message, (ControllerCallback)callback));
}
public void doWaitForFinish (WaitForFinishCommand task) {
public void doWaitForFinish (WaitForFinishUICommand command) {
logger.debug("UI entered doWaitForFinish");
startWaitingTime = new Date();
UIElement message = task.getMessage();
UIElement message = command.getMessage();
String messageString;
if (message.getType() != UIElementDataType.TEXT) {
messageString = "Default message: encountered an error. System halting";
@ -240,39 +231,57 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
@Override
public void showErrorMessageAndHalt(UIElement errorMessage, FutureCallback<Boolean> callback) {
logger.debug("UI interface call to showErrorMessageAndHalt");
queue.add(new HaltCommand(errorMessage, (ControllerCallback)callback));
throw new UnsupportedOperationException("Not implemented becuase currently not sure if we ever use it.");
}
private void doHalt (HaltCommand task) {
logger.debug("UI entered doHalt");
UIElement errorMessage = task.getErrorMessage();
@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)callback));
}
private void doFatalError (FatalErrorUICommand command) {
logger.debug("UI entered doFatalError");
UIElement errorMessage = command.getErrorMessage();
String errorMessageString;
if (errorMessage.getType() != UIElementDataType.TEXT) {
errorMessageString = "Default message: encountered an error. System halting";
} else {
errorMessageString = bytesToString(errorMessage.getData());
}
System.out.println(errorMessageString);
while (true) {
try {
wait();
} catch (InterruptedException e) {
logger.error("UI halt has been interrupted. Details: " + e);
ControllerCallback callback = task.getCallback();
assert callback instanceof HaltCallback;
((HaltCallback) callback).onFailure(e);
UIElement[] buttonLabels = command.getButtonLabels();
String[] buttonLabelStrings = new String[buttonLabels.length];
for (int i = 0; i < buttonLabels.length; ++i) {
if (buttonLabels[i].getType() != UIElementDataType.TEXT) {
buttonLabelStrings[i] = "<NON_TEXT>";
} else {
buttonLabelStrings[i] = bytesToString(errorMessage.getData());
}
}
System.out.println(errorMessageString);
for (int i = 0; i < buttonLabelStrings.length; ++i) {
System.out.println("" + i + " - " + buttonLabelStrings[i]);
}
try {
String s = readInputLine();
Integer chosenButton = new Integer(s);
command.getCallback().onSuccess(chosenButton);
}
catch (IOException e) {
String err = "doFatalError: some error with reading input from console. details: " + e;
logger.error(err);
command.getCallback().onFailure(e);
}
}
@Override
public void showErrorMessageWithButtons(UIElement errorMessage, UIElement[] buttonLabels, FutureCallback<Integer> callback) {
throw new UnsupportedOperationException();
}
private void doTick () {
if (startWaitingTime != null) {
System.out.print ("."); // still waiting
@ -295,9 +304,9 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
}
private void assertQuestionsAreValid (BallotQuestion[] questions) {
for (int index = 0; index < questions.length; ++index) {
BallotQuestion question = questions[index];
private void assertQuestionsAreValid (List<BallotQuestion> questions) {
for (int index = 0; index < questions.size(); ++index) {
BallotQuestion question = questions.get(index);
if (question.getIsMandatory()) {
String errorMessage = "askVoterQuestions: question number " + index + " is marked as mandatory";
logger.error(errorMessage);
@ -311,14 +320,14 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
}
}
private BallotAnswer[] askVoterForAnswers(BallotQuestion[] questions) throws IOException {
private List<BallotAnswer> askVoterForAnswers(List<BallotQuestion> questions) throws IOException {
assertQuestionsAreValid (questions);
BallotAnswer answers[] = new BallotAnswer[questions.length];
List<BallotAnswer> answers = new ArrayList<>();
int index = 0;
while (index < questions.length) {
BallotQuestion question = questions[index];
while (index < questions.size()) {
BallotQuestion question = questions.get(index);
System.out.println("Question number " + index);
showQuestionOnScreen(question);
@ -330,13 +339,14 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
}
else if (s.equals("back") || s.equals("b")) {
--index;
answers.remove(index);
}
else if (s.equals("skip") || s.equals("s")) {
answers[index] = translateStringAnswerToProtoBufMessageAnswer("");
answers.add(translateStringAnswerToProtoBufMessageAnswer(""));
++index;
}
else {
answers[index] = translateStringAnswerToProtoBufMessageAnswer(s);
answers.add(translateStringAnswerToProtoBufMessageAnswer(s));
++index;
}
}

View File

@ -3,6 +3,8 @@ package meerkat.voting.ui;
import com.google.common.util.concurrent.FutureCallback;
import meerkat.protobuf.Voting.*;
import java.util.List;
/**
* An interface for the user interface component of the voting booth
@ -25,14 +27,14 @@ public interface VotingBoothUI {
* @param questions questions to determine the right voting channel for this voter
* @param callback that's where we store the answers to decide channel upon for the current voter
*/
public void chooseChannel (BallotQuestion[] questions, FutureCallback<BallotAnswer[]> callback);
public void chooseChannel (List<BallotQuestion> questions, FutureCallback<List<BallotAnswer>> callback);
/**
* Presents the set of questions to the voter. Collect all his responses.
* @param questions all ballot questions to present to the voter
* @param callback the responses to the questions collected by the UI, to send back to the controller. Responses are null if voter chose to cancel session
*/
public void askVoterQuestions (BallotQuestion[] questions, FutureCallback<BallotAnswer[]> callback);
public void askVoterQuestions (List<BallotQuestion> questions, FutureCallback<List<BallotAnswer>> callback);
/**
* Get a response from the voter on how to finalize the ballot.
@ -51,7 +53,7 @@ public interface VotingBoothUI {
* @param message a message to show the user on the UI device while waiting
* @param callback a success return value of the wait (cancelling returns false)
*/
public void notifyVoterToWaitForFinish (UIElement message, FutureCallback<Boolean> callback);
public void notifyVoterToWaitForFinish (UIElement message, FutureCallback<Void> callback);
/**
* show a fatal error message in the UI. Halts system. Waits for administrator interrupt or reset

View File

@ -1,6 +1,6 @@
package meerkat.voting.ui.uicommands;
import meerkat.voting.controller.callbacks.ControllerCallback;
import meerkat.voting.controller.callbacks.*;
/**
* Created by hai on 21/04/16.

View File

@ -1,22 +1,24 @@
package meerkat.voting.ui.uicommands;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.callbacks.ControllerCallback;
import meerkat.voting.controller.callbacks.*;
import java.util.List;
/**
* Created by hai on 18/04/16.
*/
public class ChannelChoiceUICommand extends UICommand {
private final BallotQuestion[] questions;
private final List<BallotQuestion> questions;
public ChannelChoiceUICommand(BallotQuestion[] questions, ControllerCallback callback)
public ChannelChoiceUICommand(List<BallotQuestion> questions, ControllerCallback callback)
{
super(callback);
this.questions = questions;
}
public BallotQuestion[] getQuestions () {
public List<BallotQuestion> getQuestions () {
return this.questions;
}
}

View File

@ -0,0 +1,28 @@
package meerkat.voting.ui.uicommands;
import meerkat.protobuf.Voting.UIElement;
import meerkat.voting.controller.callbacks.*;
/**
* Created by hai on 18/04/16.
*/
public class FatalErrorUICommand extends UICommand {
private final UIElement errorMessage;
private final UIElement[] buttonLabels;
public FatalErrorUICommand(UIElement errorMessage, UIElement[] buttonLabels, ControllerCallback callback)
{
super(callback);
this.errorMessage = errorMessage;
this.buttonLabels = buttonLabels;
}
public UIElement getErrorMessage() {
return this.errorMessage;
}
public UIElement[] getButtonLabels() {
return this.getButtonLabels();
}
}

View File

@ -1,22 +0,0 @@
package meerkat.voting.ui.uicommands;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.callbacks.ControllerCallback;
/**
* Created by hai on 18/04/16.
*/
public class HaltCommand extends UICommand {
private final UIElement errorMessage;
public HaltCommand(UIElement errorMessage, ControllerCallback callback)
{
super(callback);
this.errorMessage = errorMessage;
}
public UIElement getErrorMessage () {
return this.errorMessage;
}
}

View File

@ -1,22 +1,24 @@
package meerkat.voting.ui.uicommands;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.callbacks.ControllerCallback;
import meerkat.voting.controller.callbacks.*;
import java.util.List;
/**
* Created by hai on 18/04/16.
*/
public class RaceVotingUICommand extends UICommand {
private final BallotQuestion[] questions;
private final List<BallotQuestion> questions;
public RaceVotingUICommand(BallotQuestion[] questions, ControllerCallback callback)
public RaceVotingUICommand(List<BallotQuestion> questions, ControllerCallback callback)
{
super(callback);
this.questions = questions;
}
public BallotQuestion[] getQuestions () {
public List<BallotQuestion> getQuestions () {
return this.questions;
}
}

View File

@ -1,6 +1,6 @@
package meerkat.voting.ui.uicommands;
import meerkat.voting.controller.callbacks.ControllerCallback;
import meerkat.voting.controller.callbacks.*;
/**
* Created by hai on 18/04/16.

View File

@ -1,6 +1,6 @@
package meerkat.voting.ui.uicommands;
import meerkat.voting.controller.callbacks.ControllerCallback;
import meerkat.voting.controller.callbacks.*;
/**
* Created by hai on 18/04/16.

View File

@ -1,6 +1,6 @@
package meerkat.voting.ui.uicommands;
import meerkat.voting.controller.callbacks.ControllerCallback;
import meerkat.voting.controller.callbacks.*;
//TODO: make this class generic
public abstract class UICommand {

View File

@ -1,17 +1,17 @@
package meerkat.voting.ui.uicommands;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.callbacks.ControllerCallback;
import meerkat.voting.controller.callbacks.*;
/**
* Created by hai on 18/04/16.
*/
public class WaitForFinishCommand extends UICommand {
public class WaitForFinishUICommand extends UICommand {
private final UIElement message;
public WaitForFinishCommand(UIElement message, ControllerCallback callback)
public WaitForFinishUICommand(UIElement message, ControllerCallback callback)
{
super(callback);
this.message = message;