Just added comments as part of the process to comment all the VB files.

Currently I commented the controller callbacks and commands packages, and
also the QuestionSelector component.
vbdev2
Hai Brenner 2016-06-21 15:37:20 +03:00
parent e9732561f4
commit 42ae18df00
23 changed files with 168 additions and 54 deletions

View File

@ -2,13 +2,16 @@ package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting.UIElement;
import meerkat.voting.controller.commands.*;
import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.ui.VotingBoothUI.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* A controller callback for the cast-or-audit request to the UI.
* Upon getting a FinalizeBallotChoice response from the voter, the callback then registers a new command
* to the controller queue, either a CastCommand or an AuditCommand according to the voter's choice
*/
public class CastOrAuditCallback extends ControllerCallback<FinalizeBallotChoice> {
protected final static Logger logger = LoggerFactory.getLogger(CastOrAuditCallback.class);
protected final UIElement unrecognizedFinalizeResponseMessage;
@ -31,9 +34,7 @@ public class CastOrAuditCallback extends ControllerCallback<FinalizeBallotChoice
}
else {
logger.error("CastOrAuditCallback got an unrecognized response: " + result);
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
unrecognizedFinalizeResponseMessage));
onFailure(new IllegalArgumentException("CastOrAuditCallback got an unknown result (" + result + ")"));
}
}

View File

@ -4,12 +4,15 @@ import meerkat.protobuf.Voting.*;
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.
* A controller callback for the channel-choice request to the UI.
* Upon receiving the answers for the channel-choice questions, the callback registers a new ChannelDeterminedCommand
* to the controller queue, so the controller can then process the answers and set the channel.
* If voter cancelled during the process, a cancelling exception is thrown and a RestartVotingCommand is
* registered through the onFailure() method
*/
public class ChannelChoiceCallback extends ControllerCallback<List<BallotAnswer>> {
protected final static Logger logger = LoggerFactory.getLogger(ChannelChoiceCallback.class);
@ -26,12 +29,14 @@ public class ChannelChoiceCallback extends ControllerCallback<List<BallotAnswer>
@Override
public void onSuccess(List<BallotAnswer> result) {
logger.debug("callback for channel choice returned success");
// register the chosen BallotAnswers to a command in the controller queue
enqueueCommand(new ChannelDeterminedCommand(getRequestIdentifier(), getBallotSerialNumber(), result));
}
@Override
public void onFailure(Throwable t) {
if (t instanceof VoterCancelException) {
// voter has cancelled during the UI channel choice process. A VoterCancelException is thrown
logger.debug("ChannelChoiceCallback got a cancellation response");
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}

View File

@ -1,13 +1,17 @@
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;
/**
* The base (abstract) class of all callbacks for requests sent by the controller to other components (ui, output-device)
* It implements the FutureCallback interface
* Its members are:
* - requestIdentifier - uniquely identifies the request which this callback responds
* - ballotSerialNumber - number of ballot which was currently active when request was sent
* - controllerQueue - so the callback can issue and register a new command to the controller, once the request handling is finished
*/
public abstract class ControllerCallback<T> implements FutureCallback<T> {
private final int requestIdentifier;

View File

@ -1,14 +1,16 @@
package meerkat.voting.controller.callbacks;
import jdk.nashorn.internal.runtime.regexp.joni.exception.ValueException;
import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.RestartVotingCommand;
import meerkat.voting.controller.commands.RetryEncryptAndCommitBallotCommand;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* This is quite a special callback. It is not issued in a normal flow of the voting.
* This callback is made only for a request to the UI to choose handling of failure in encryption.
* When encryption/signature fails the voter is asked in the UI whether to retry or abort.
* This specific callback decides, upon the answer to this request, which command to register in the controller's queue
*/
public class EncryptionFailedCallback extends ControllerCallback<Integer> {
protected final static Logger logger = LoggerFactory.getLogger(EncryptionFailedCallback.class);
@ -20,18 +22,18 @@ public class EncryptionFailedCallback extends ControllerCallback<Integer> {
@Override
public void onSuccess(Integer result) {
logger.debug("callback for voting returned success");
logger.debug("callback for encryption-failed request is initiated successfully");
int res = result.intValue();
if (res == 0) {
logger.debug("voter chose to retry encryption");
enqueueCommand(new RetryEncryptAndCommitBallotCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
else if (res == 1) {
logger.debug("voter chose to cancel the vote");
logger.debug("voter chose to abort the vote");
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
else {
onFailure(new ValueException("EncryptionFailedCallback got an unknown result (" + res + ")"));
onFailure(new IllegalArgumentException("EncryptionFailedCallback got an unknown result (" + res + ")"));
}
}

View File

@ -1,12 +1,16 @@
package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.RestartVotingCommand;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* This is quite a special callback. It is not issued in a normal flow of the voting.
* This callback is made only for a request to the UI to show the voter an error message.
* Upon approval of the voter, the method onSuccess() of this callback is called, and the voting
* is reset through a command to the controller's queue
*/
public class ErrorMessageRestartCallback extends ControllerCallback<Integer> {
protected final static Logger logger = LoggerFactory.getLogger(ErrorMessageRestartCallback.class);

View File

@ -1,13 +1,16 @@
package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.commands.ChannelChoiceCommand;
import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.RestartVotingCommand;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* A controller callback for the StartSession request to the UI.
* Upon approval of the voter, it registers a new ChannelChoiceCommand to the controller queue (which
* then starts the channel choice process)
*/
public class NewVoterCallback extends ControllerCallback<Void> {
protected final static Logger logger = LoggerFactory.getLogger(NewVoterCallback.class);

View File

@ -1,14 +1,16 @@
package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting.UIElement;
import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.ChooseFinalizeOptionCommand;
import meerkat.voting.controller.commands.ReportErrorCommand;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* A controller callback for the Commit request to the output-device.
* When committing is done, the callback's onSuccess() method is called to register a new ChooseFinalizeOptionCommand
* to the controller
*/
public class OutputDeviceCommitCallback extends ControllerCallback<Void> {
protected final static Logger logger = LoggerFactory.getLogger(OutputDeviceCommitCallback.class);
protected final UIElement outputDeviceFailureMessage;

View File

@ -1,14 +1,16 @@
package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting.UIElement;
import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.ReportErrorCommand;
import meerkat.voting.controller.commands.RestartVotingCommand;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* A controller callback for the Finalize request to the output-device.
* When finalizing (either cast or audit) is done,
* the callback's onSuccess() method is called to register a new command to the controller to restart the voting process
*/
public class OutputDeviceFinalizeCallback extends ControllerCallback<Void> {
protected final static Logger logger = LoggerFactory.getLogger(OutputDeviceFinalizeCallback.class);
protected final UIElement outputDeviceFailureMessage;

View File

@ -1,8 +1,8 @@
package meerkat.voting.controller.callbacks;
/**
* Created by hai on 06/06/16.
* Just a simple unique exception to throw when a voter aborts/cancels the voting during the voting process
*/
public class VoterCancelException extends Exception{
public class VoterCancelException extends Exception {
//
}

View File

@ -1,16 +1,19 @@
package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting.*;
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 meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
/**
* A controller callback for the race-voting request to the UI.
* Upon receiving the answers for the race questions, the callback registers a new command to process
* the voter's answers (encrypt and then commit) into the controller's queue.
* If voter cancelled during the process, a cancelling exception is thrown and a RestartVotingCommand is
* registered through the onFailure() method
*/
public class VotingCallback extends ControllerCallback<List<BallotAnswer>> {
protected final static Logger logger = LoggerFactory.getLogger(VotingCallback.class);
protected final UIElement unsuccessfulVotingMessage;

View File

@ -1,13 +1,16 @@
package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting.UIElement;
import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.ReportErrorCommand;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* This callback is attached to requests to UI which ask the voter to wait for some process to finish.
* It actually asks nothing in the UI, and it is simply attached to the UI request as a place-holder.
* Therefore its onSuccess() method is empty
*/
public class WaitForFinishCallback extends ControllerCallback<Void> {
protected final static Logger logger = LoggerFactory.getLogger(WaitForFinishCallback.class);
protected final UIElement somethingWrongMessage;

View File

@ -1,7 +1,7 @@
package meerkat.voting.controller.commands;
/**
* Created by hai on 11/04/16.
* a command to audit the ballot
*/
public class AuditCommand extends ControllerCommand {
public AuditCommand(int requestIdentifier, long ballotSerialNumber) {

View File

@ -1,7 +1,7 @@
package meerkat.voting.controller.commands;
/**
* Created by hai on 11/04/16.
* a command to cast the ballot
*/
public class CastCommand extends ControllerCommand {
public CastCommand(int requestIdentifier, long ballotSerialNumber) {

View File

@ -1,7 +1,7 @@
package meerkat.voting.controller.commands;
/**
* Created by hai on 11/04/16.
* a command to initiate the channel choice flow at the beginning of the voting
*/
public class ChannelChoiceCommand extends ControllerCommand {
public ChannelChoiceCommand(int requestIdentifier, long ballotSerialNumber) {

View File

@ -4,7 +4,7 @@ import meerkat.protobuf.Voting.*;
import java.util.List;
/**
* Created by hai on 11/04/16.
* This command is registered in the controller right after the voter answered all the channel choice questions
*/
public class ChannelDeterminedCommand extends ControllerCommand {
public List<BallotAnswer> channelChoiceAnswers;

View File

@ -1,7 +1,7 @@
package meerkat.voting.controller.commands;
/**
* Created by hai on 11/04/16.
* a command to initiate asking the voter how to finalize (cast-or-audit) the ballot
*/
public class ChooseFinalizeOptionCommand extends ControllerCommand {
public ChooseFinalizeOptionCommand(int requestIdentifier, long ballotSerialNumber) {

View File

@ -1,5 +1,9 @@
package meerkat.voting.controller.commands;
/**
* This is the base class for the controller commands.
* These commands are registered in a command queue of the controller.
*/
public abstract class ControllerCommand {
protected final int requestIdentifier;
protected final long ballotSerialNumber;

View File

@ -3,6 +3,10 @@ package meerkat.voting.controller.commands;
import meerkat.protobuf.Voting.BallotAnswer;
import java.util.List;
/**
* a command registered after voter answered all ballot questions.
* The controller then initiates an encryption-signature-commit flow
*/
public class EncryptAndCommitBallotCommand extends ControllerCommand {
private final List<BallotAnswer> votingAnswers;

View File

@ -2,6 +2,10 @@ package meerkat.voting.controller.commands;
import meerkat.protobuf.Voting.*;
/**
* This command is not a part of the normal flow of the controller.
* It asks the controller to handle (report to voter) some error message
*/
public class ReportErrorCommand extends ControllerCommand {
private final UIElement errorMessage;

View File

@ -1,6 +1,9 @@
package meerkat.voting.controller.commands;
/**
* a command to restart a voting flow (for a new voter)
*/
public class RestartVotingCommand extends ControllerCommand {
public RestartVotingCommand(int requestIdentifier, long ballotSerialNumber) {
super(requestIdentifier, ballotSerialNumber);

View File

@ -1,5 +1,10 @@
package meerkat.voting.controller.commands;
/**
* This is quite a special command not part of the normal voting flow.
* It extends the base EncryptAndCommitBallotCommand for occasions where first attempt of encryption failed
* and the voter asks to re-try encrypting and committing.
*/
public class RetryEncryptAndCommitBallotCommand extends EncryptAndCommitBallotCommand {
public RetryEncryptAndCommitBallotCommand(int requestIdentifier,

View File

@ -4,12 +4,26 @@ import meerkat.protobuf.Voting.*;
import java.util.List;
/**
* Created by hai on 02/05/16.
* An interface for the question-selection component.
* This component handles the connection between the channel choice questions and the race questions.
* It gets the answers for the channel choice questions and determines which race question to put in the voter's ballot.
* It also creates an identifier for this chosen channel. This identifier should appear in the plaintext of the ballot.
* The channel identifier does not identify a specific voter, but rather it identifies a specific voting channel
*/
public interface QuestionSelector {
/**
* determines an identifier for the channel of the voter
* @param channelChoiceAnswers The answers given by the voter to the channel choice questions
* @return an identifier of the channel. To be used by selectQuestionsForVoter(). This identifier should also appear on the plaintext of the ballot
*/
public byte[] getChannelIdentifier (List<BallotAnswer> channelChoiceAnswers);
/**
* determines which race questions to present to the voter according to its channel
* @param channelIdentifier the identifier of this specific channel
* @return the race questions (to present to the voter)
*/
public List<BallotQuestion> selectQuestionsForVoter (byte[] channelIdentifier);
}

View File

@ -11,28 +11,47 @@ import java.lang.Math;
/**
* 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.
* Every category is an array of ballot race questions.
* Data of categories is initialized and stored by a SimpleCategoriesSelectionData protobuf.
* 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)
* is included in the ballot if its index appears in any chosen category, or in the default category shared by all voters)
*/
public class SimpleListCategoriesSelector implements QuestionSelector {
protected final static Logger logger = LoggerFactory.getLogger(SimpleListCategoriesSelector.class);
// all the possible race questions
private final BallotQuestion[] allBallotQuestions;
// this category is presented to any voter (regardless of his answers to the channel choice questions)
private final int[] sharedDefaults;
// all the categories.
// first index is the channel choice question number
// second index is a possible answer to this question
// categoryChoosers[questionNumber][answerNumber] is an array of indices (to the ballotQuestions array).
// This category of questions is included in the ballot if voter answered this specific answer to this channel choice question
private final int[][][] categoryChoosers;
private final static byte QUESTION_SELECTED = (byte)1;
private final static byte QUESTION_NOT_SELECTED = (byte)0;
/**
* A very straight-forward constructor for the SimpleListCategoriesSelector
* @param allBallotQuestions all possible race questions for this election
* @param data a protobuf containing all the index categories
*/
public SimpleListCategoriesSelector(List<BallotQuestion> allBallotQuestions, SimpleCategoriesSelectionData data) {
// copies the ballot race question list into a member array
this.allBallotQuestions = new BallotQuestion[allBallotQuestions.size()];
allBallotQuestions.toArray(this.allBallotQuestions);
// copies the shared category list (as appears in the protobuf data) into a member array
sharedDefaults = listToIntArray(data.getSharedDefaults().getQuestionIndexList());
// copies the category lists (as appear in the protobuf data) into a 3-dimensional member array
int[][][] selectionDataTmp = new int[data.getCategoryChooserList().size()][][];
int channelChoiceQuestionNumber = 0;
for (CategoryChooser catChooser: data.getCategoryChooserList()) {
@ -45,12 +64,21 @@ public class SimpleListCategoriesSelector implements QuestionSelector {
++channelChoiceQuestionNumber;
}
categoryChoosers = selectionDataTmp;
// verifies in advance that there are not very suspicious indices in the selection data
assertDataValid();
}
/*
* asserts that the selection data does not contain a question index which is beyond the length of
* the ballot race questions array. Otherwise, throws an IndexOutOfBoundsException
*/
private void assertDataValid () {
int questionsLength = allBallotQuestions.length;
// find the maximum question index in the selection data
int maxQuestionIndex = -1;
for (int index: sharedDefaults) {
maxQuestionIndex = Math.max(maxQuestionIndex, index);
}
for (int[][] categoryChooser: categoryChoosers) {
for (int[] category: categoryChooser) {
for (int index: category) {
@ -58,6 +86,9 @@ public class SimpleListCategoriesSelector implements QuestionSelector {
}
}
}
// asserts that the maximal question index in the selection data does not overflow the ballot race questions array
int questionsLength = allBallotQuestions.length;
if (maxQuestionIndex >= questionsLength) {
String errorMessage = "Selection data refers to question index " + maxQuestionIndex + " while we have only " + questionsLength + " questions totally";
logger.error(errorMessage);
@ -66,8 +97,21 @@ public class SimpleListCategoriesSelector implements QuestionSelector {
}
/**
* an implementation of the QuestionSelector interface method.
* In this selector class the identifier simply marks all the ballot race questions which appear in at least one
* category of the categories chosen by the voter (or in the shared defaults category) in the channel choice round.
* @param channelChoiceAnswers The answers given by the voter to the channel choice questions
* @return the channel identifier
*/
@Override
public byte[] getChannelIdentifier(List<BallotAnswer> channelChoiceAnswers) {
/*
* Currently, this implementation of the QuestionSelector interface returns an over-simplified identifier which
* is merely an array of booleans (which flags the questions to appear in the ballot)
* For elections with more than one possible channel we should return a more printable and recognizable
* identifier to be put in the plaintext of the ballot
*/
byte[] isSelected = new byte[allBallotQuestions.length];
java.util.Arrays.fill(isSelected, QUESTION_NOT_SELECTED);
@ -86,6 +130,10 @@ public class SimpleListCategoriesSelector implements QuestionSelector {
return isSelected;
}
/*
* Verifies that the ballot answer is of length 1. (We do not yet handle multi-choice questions in the channel choice round).
* Otherwise, throws an exception.
*/
private void assertAnswerLengthIsOne (BallotAnswer ballotAnswer, int questionNumber) {
if (ballotAnswer.getAnswerCount() != 1) {
String errorMessage = "SimpleListCategoriesSelector expects a single answer for every channel choice question\n";
@ -109,6 +157,9 @@ public class SimpleListCategoriesSelector implements QuestionSelector {
return selectedQuestions;
}
/*
* copies a List of Integers into an int[] array of same length
*/
private int[] listToIntArray(List<Integer> l) {
int[] res = new int[l.size()];
int index = 0;