Refactor: add comments to VotingBoothImpl.java, and rename tasks to commands (because they were two names of the same thing)
Signed-off-by: Hai Brenner <haibrenner@gmail.com>vbdev2
parent
42d68b7ce8
commit
0956fa98d3
|
@ -24,29 +24,30 @@ import java.util.concurrent.LinkedBlockingQueue;
|
|||
*/
|
||||
public class VotingBoothImpl implements VotingBoothController {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(VotingBoothImpl.class);;
|
||||
|
||||
// the component interfaces of the Voting Booth
|
||||
private BallotOutputDevice outputDevice;
|
||||
private VBCryptoManager crypto;
|
||||
private VotingBoothUI ui;
|
||||
private StorageManager storageManager;
|
||||
|
||||
// election details and info
|
||||
private List<BallotQuestion> questionsForChoosingChannel;
|
||||
private List<BallotQuestion> questions;
|
||||
private QuestionSelector questionSelector;
|
||||
private Map<String, UIElement> systemMessages;
|
||||
|
||||
private LinkedBlockingQueue<ControllerCommand> queue;
|
||||
|
||||
private Logger logger;
|
||||
|
||||
// state
|
||||
private ControllerState state;
|
||||
private volatile boolean shutDownHasBeenCalled;
|
||||
|
||||
private LinkedBlockingQueue<ControllerCommand> queue;
|
||||
protected final int MAX_REQUEST_IDENTIFIER = 100000;
|
||||
private static int requestCounter = 0;
|
||||
|
||||
|
||||
|
||||
// a simple constructor
|
||||
public VotingBoothImpl () {
|
||||
logger = LoggerFactory.getLogger(VotingBoothImpl.class);
|
||||
logger.info("A VotingBoothImpl is constructed");
|
||||
shutDownHasBeenCalled = false;
|
||||
queue = new LinkedBlockingQueue<>();
|
||||
|
@ -59,11 +60,14 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
VotingBoothUI vbUI,
|
||||
StorageManager vbStorageManager) throws IOException {
|
||||
logger.info("init is called");
|
||||
|
||||
// keep pointers to the VB components
|
||||
this.outputDevice = outputDevice;
|
||||
this.crypto = vbCrypto;
|
||||
this.ui = vbUI;
|
||||
this.storageManager = vbStorageManager;
|
||||
|
||||
// store election details and info
|
||||
ElectionParams electionParams;
|
||||
try {
|
||||
logger.info("init: reading election params");
|
||||
|
@ -92,6 +96,10 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* a method for running the Voting flow of the VB (in contrast to the admin-setup flow
|
||||
* It simply loops: takes the next command in its inner queue and handles it
|
||||
*/
|
||||
private void runVotingFlow () {
|
||||
logger.info("entered the voting flow");
|
||||
|
||||
|
@ -99,11 +107,11 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
|
||||
while (! wasShutDownCalled()) {
|
||||
try {
|
||||
ControllerCommand task = queue.take();
|
||||
handleSingleTask (task);
|
||||
ControllerCommand 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,41 +125,48 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
outputDevice.callShutDown();
|
||||
}
|
||||
|
||||
private void handleSingleTask (ControllerCommand task) {
|
||||
if (task.getBallotSerialNumber() != state.currentBallotSerialNumber && !(task instanceof RestartVotingCommand)) {
|
||||
/**
|
||||
* this method decides upon a given command if to ignore it (if it has an old serial number) or to handle it
|
||||
* If we choose to handle it, then it simply calls the matching method which handles this type of command
|
||||
* @param command a command to handle next (probably from the inner command queue)
|
||||
*/
|
||||
private void handleSingleCommand(ControllerCommand command) {
|
||||
// check if the command is old and should be ignored
|
||||
if (command.getBallotSerialNumber() != state.currentBallotSerialNumber && !(command 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;
|
||||
String errorMessage = "handleSingleCommand: received a task too old. " +
|
||||
command.getBallotSerialNumber() + " " + state.currentBallotSerialNumber;
|
||||
logger.debug(errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (task instanceof RestartVotingCommand) {
|
||||
// decide which method to run according to the command type
|
||||
if (command instanceof RestartVotingCommand) {
|
||||
doRestartVoting ();
|
||||
}
|
||||
else if (task instanceof ChannelChoiceCommand) {
|
||||
else if (command instanceof ChannelChoiceCommand) {
|
||||
doChooseChannel();
|
||||
}
|
||||
else if (task instanceof ChannelDeterminedCommand) {
|
||||
doSetChannelAndAskQuestions ((ChannelDeterminedCommand)task);
|
||||
else if (command instanceof ChannelDeterminedCommand) {
|
||||
doSetChannelAndAskQuestions ((ChannelDeterminedCommand)command);
|
||||
}
|
||||
else if (task instanceof ChooseFinalizeOptionCommand) {
|
||||
else if (command instanceof ChooseFinalizeOptionCommand) {
|
||||
doChooseFinalizeOption();
|
||||
}
|
||||
else if (task instanceof CastCommand) {
|
||||
else if (command instanceof CastCommand) {
|
||||
doFinalize(false);
|
||||
}
|
||||
else if (task instanceof AuditCommand) {
|
||||
else if (command instanceof AuditCommand) {
|
||||
doFinalize(true);
|
||||
}
|
||||
else if (task instanceof EncryptAndCommitBallotCommand) {
|
||||
doCommit ((EncryptAndCommitBallotCommand)task);
|
||||
else if (command instanceof EncryptAndCommitBallotCommand) {
|
||||
doCommit ((EncryptAndCommitBallotCommand)command);
|
||||
}
|
||||
else if (task instanceof ReportErrorCommand) {
|
||||
doReportErrorAndForceRestart((ReportErrorCommand)task);
|
||||
else if (command instanceof ReportErrorCommand) {
|
||||
doReportErrorAndForceRestart((ReportErrorCommand)command);
|
||||
}
|
||||
else {
|
||||
logger.error("handleSingleTask: unknown type of ControllerCommand received: " + task.getClass().getName());
|
||||
logger.error("handleSingleCommand: unknown type of ControllerCommand received: " + command.getClass().getName());
|
||||
doReportErrorAndForceRestart(systemMessages.get(StorageManager.SOMETHING_WRONG_MESSAGE));
|
||||
}
|
||||
}
|
||||
|
@ -166,17 +181,28 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
//TODO: add commands to actually shut down the machine
|
||||
}
|
||||
|
||||
/**
|
||||
* a method to execute a Restart Voting Command
|
||||
*/
|
||||
private void doRestartVoting () {
|
||||
queue.clear();
|
||||
state.clearAndResetState(VBState.NEW_VOTER);
|
||||
|
||||
ui.startNewVoterSession(new NewVoterCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
|
||||
}
|
||||
|
||||
private void doReportErrorAndForceRestart(ReportErrorCommand task) {
|
||||
doReportErrorAndForceRestart(task.getErrorMessage());
|
||||
/**
|
||||
* a (overloaded) method to execute a Report Error Command.
|
||||
* It actually just runs the overloaded version of this method with the error message inside the command
|
||||
* @param command the command has the info of the error message to report
|
||||
*/
|
||||
private void doReportErrorAndForceRestart(ReportErrorCommand command) {
|
||||
doReportErrorAndForceRestart(command.getErrorMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* a (overloaded) method to report an error message to the voter
|
||||
* @param errorMessage message to show the voter
|
||||
*/
|
||||
private void doReportErrorAndForceRestart(UIElement errorMessage) {
|
||||
queue.clear();
|
||||
state.clearAndResetState(VBState.FATAL_ERROR_FORCE_NEW_VOTER);
|
||||
|
@ -187,6 +213,10 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
this.queue));
|
||||
}
|
||||
|
||||
/**
|
||||
* a method to execute a Channel Choice Command
|
||||
* it notifies the UI to present the channel choice questions to the voter
|
||||
*/
|
||||
private void doChooseChannel () {
|
||||
if (state.stateIdentifier == VBState.NEW_VOTER) {
|
||||
logger.debug("doing chooseChannel");
|
||||
|
@ -203,11 +233,17 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
}
|
||||
}
|
||||
|
||||
private void doSetChannelAndAskQuestions (ChannelDeterminedCommand task) {
|
||||
/**
|
||||
* a method to execute a Channel Determined Command
|
||||
* (this actually sets the channel now after the voter has answered the channel choice questions)
|
||||
* It then determines the race questions for the voter, and notifies the UI to present them to the voter
|
||||
* @param command details of the voter's answers on the channel choice questions
|
||||
*/
|
||||
private void doSetChannelAndAskQuestions (ChannelDeterminedCommand command) {
|
||||
if (state.stateIdentifier == VBState.CHOOSE_CHANNEL) {
|
||||
logger.debug("doing set channel and ask questions");
|
||||
state.stateIdentifier = VBState.ANSWER_QUESTIONS;
|
||||
List<BallotAnswer> channelChoiceAnswers = task.channelChoiceAnswers;
|
||||
List<BallotAnswer> channelChoiceAnswers = command.channelChoiceAnswers;
|
||||
state.channelIdentifier = questionSelector.getChannelIdentifier(channelChoiceAnswers);
|
||||
state.channelSpecificQuestions = questionSelector.selectQuestionsForVoter(state.channelIdentifier);
|
||||
ui.askVoterQuestions(state.channelSpecificQuestions,
|
||||
|
@ -222,7 +258,10 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* a method to execute a Do-Finalzie-Option Command
|
||||
* notifies the UI to present the cast-or-audit question to the voter
|
||||
*/
|
||||
private void doChooseFinalizeOption() {
|
||||
if (state.stateIdentifier == VBState.COMMITTING_TO_BALLOT) {
|
||||
logger.debug("doChooseFinalizeOption");
|
||||
|
@ -237,11 +276,17 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
// ignore this request
|
||||
}
|
||||
}
|
||||
private void doCommit (EncryptAndCommitBallotCommand task) {
|
||||
|
||||
/**
|
||||
* a method to execute a Encrypt-and-Commit Command
|
||||
* It sends a notification to commit to the output device
|
||||
* @param command details of the voter's answers on the ballot questions
|
||||
*/
|
||||
private void doCommit (EncryptAndCommitBallotCommand command) {
|
||||
if (state.stateIdentifier == VBState.ANSWER_QUESTIONS) {
|
||||
logger.debug("doing commit");
|
||||
try {
|
||||
setBallotData(task);
|
||||
setBallotData(command);
|
||||
ui.notifyVoterToWaitForFinish(systemMessages.get(StorageManager.WAIT_FOR_COMMIT_MESSAGE),
|
||||
new WaitForFinishCallback(generateRequestIdentifier(),
|
||||
state.currentBallotSerialNumber,
|
||||
|
@ -257,6 +302,9 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
}
|
||||
catch (SignatureException | IOException e) {
|
||||
logger.error("doCommit: encryption failed. exception: " + e);
|
||||
|
||||
// in case the encryption failed for some unknown reason, we send the UI a notification
|
||||
// to ask the voter whether he wants to retry or cancel the ballot
|
||||
UIElement errorMessage = systemMessages.get(StorageManager.ENCRYPTION_FAILED_MESSAGE);
|
||||
UIElement[] buttons = new UIElement[]{
|
||||
systemMessages.get(StorageManager.RETRY_BUTTON),
|
||||
|
@ -274,25 +322,41 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
}
|
||||
}
|
||||
|
||||
private void setBallotData (EncryptAndCommitBallotCommand task) throws IOException, SignatureException{
|
||||
if (! (task instanceof RetryEncryptAndCommitBallotCommand)) {
|
||||
/**
|
||||
* encrypt the ballot, and keep all info (plaintext, encryption and secrets) in the state's attributes
|
||||
* @param command either an EncryptAndCommitBallotCommand if we encrypt the plaintext for the first time, or a RetryEncryptAndCommitBallotCommand if we already got here but encryption failed before
|
||||
* @throws IOException problems in the encryption process
|
||||
* @throws SignatureException problems in the digital signature process
|
||||
*/
|
||||
private void setBallotData (EncryptAndCommitBallotCommand command) throws IOException, SignatureException{
|
||||
// a Retry command is given only if we first got here, and later the encryption failed but the voter chose to retry
|
||||
// in such a case the plaintext is already set from previous attempt
|
||||
if (! (command instanceof RetryEncryptAndCommitBallotCommand)) {
|
||||
// this is not a retry attempt, so the plaintext is not set yet
|
||||
// otherwise, we have the plaintext from the previous encryption attempt
|
||||
state.plaintextBallot = PlaintextBallot.newBuilder()
|
||||
.setSerialNumber(task.getBallotSerialNumber())
|
||||
.addAllAnswers(task.getVotingAnswers())
|
||||
.setSerialNumber(command.getBallotSerialNumber())
|
||||
.addAllAnswers(command.getVotingAnswers())
|
||||
.build();
|
||||
}
|
||||
|
||||
// keep the encryption and the secrets we used for it
|
||||
EncryptionAndSecrets encryptionAndSecrets = crypto.encrypt(state.plaintextBallot);
|
||||
state.signedEncryptedBallot = encryptionAndSecrets.getSignedEncryptedBallot();
|
||||
state.secrets = encryptionAndSecrets.getSecrets();
|
||||
}
|
||||
|
||||
/**
|
||||
* a method to execute a Cast Command or an Audit Command
|
||||
* according to the flag, chooses which finalize ballot task to send to the output device
|
||||
* @param auditRequested true if we wish to finalize by auditing. false if we finalize by casting the ballot
|
||||
*/
|
||||
private void doFinalize (boolean auditRequested) {
|
||||
if (state.stateIdentifier == VBState.CAST_OR_AUDIT) {
|
||||
logger.debug("finalizing");
|
||||
state.stateIdentifier = VBState.FINALIZING;
|
||||
if (auditRequested) {
|
||||
// finalize by auditing
|
||||
ui.notifyVoterToWaitForFinish(systemMessages.get(StorageManager.WAIT_FOR_AUDIT_MESSAGE),
|
||||
new WaitForFinishCallback(generateRequestIdentifier(),
|
||||
state.currentBallotSerialNumber,
|
||||
|
@ -305,6 +369,7 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
systemMessages.get(StorageManager.OUTPUT_DEVICE_FAILURE_MESSAGE)));
|
||||
}
|
||||
else {
|
||||
// finalize by casting the ballot
|
||||
ui.notifyVoterToWaitForFinish(systemMessages.get(StorageManager.WAIT_FOR_CAST_MESSAGE),
|
||||
new WaitForFinishCallback(generateRequestIdentifier(),
|
||||
state.currentBallotSerialNumber,
|
||||
|
@ -325,8 +390,7 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
// an enum to keep the step (of the voting process) in which the VB is currently in
|
||||
private enum VBState {
|
||||
NEW_VOTER,
|
||||
CHOOSE_CHANNEL,
|
||||
|
@ -339,6 +403,15 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* a class to keep and directly access all the details of the VB controller state.
|
||||
* naming:
|
||||
* - the (enum) state identifier of the current step
|
||||
* - the chosen channel
|
||||
* - all details of the ballot (both plaintext and encryption)
|
||||
* - last request identifier (to one of the other component interfaces)
|
||||
* - serial number of the current ballot
|
||||
*/
|
||||
private class ControllerState {
|
||||
public VBState stateIdentifier;
|
||||
public byte[] channelIdentifier;
|
||||
|
@ -359,7 +432,6 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
currentBallotSerialNumber = 0;
|
||||
}
|
||||
|
||||
|
||||
private void clearPlaintext () {
|
||||
plaintextBallot = null;
|
||||
}
|
||||
|
@ -379,6 +451,11 @@ public class VotingBoothImpl implements VotingBoothController {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new request identifier to identify any call to one of the other component interfaces.
|
||||
* We limit its value to MAX_REQUEST_IDENTIFIER, so the identifier is kept short.
|
||||
* @return a new request identifier
|
||||
*/
|
||||
private int generateRequestIdentifier() {
|
||||
++requestCounter;
|
||||
if (requestCounter >= MAX_REQUEST_IDENTIFIER) {
|
||||
|
|
Loading…
Reference in New Issue