The client must create its inputstream before it creates its outputstream.
If a class/program requires the path to the election object then this 'path' this defined to be the location of the object as opposed to the location and filename. For example: /election-base/
If either or both of the servers go down during the election then we would advise the people running the election to restart it from scratch. The servers are not designed to automatically recover their states and continue after a crash. This could however be a improvement that could be made in future if this project were to be continued by others.
We have added a method in the election class that will perform checking to ensure that the people running the election/ the setup program has setup necessary things such as created output directories
The extensions of the serialized CountControlForms and CandidateRecordForms will be in lowercase rather than uppercase, this is just for consistency with the general naming of files under UNIX. Furthermore, since there could possibly be a large number of these files, they will now be saved in the /election-base/formdata/ directory. The OutputResults program has been modified to look for these files in this directory. This and all other such directories (such as the public_html for the election results) will be created by the running GenerateElectionData.
Some additional modifications were also made during the testing phase. See the next section.
Details of the class isolation testing that took place earlier is available in the form of javadoc comments to the test class. See: http://www.thor.cam.ac.uk/group/CST1b/juliet/javadoc/
We decided to proceed directly from Phase One Testing to Phase Three Testing. This decision was not taken lightly, and it was not taken as a means to take time. Instead it was taken after reassessing the practicality of testing the system. Testing the system as a whole would not be particularly different from testing it in clusters because the system naturally divides into three clusters and these three clusters do maintain some degree of separation when the system is used wholly anyway. What follows is a description of our activities when testing the system, the problems that we encountered and how we proceeded.
We started the Setup GUI and entered the details for a small election:
We saved the election object, exited the setup GUI and restarted it. We reloaded the elation object. All the data was reloaded. All though this was not an extensive test of the GUI, at this point we concluded that their were not any major problems with the Setup GUI. We were fairly sure of this beforehand because of the testing that had already taken place.
Next we ran GenerateElectionData. We received a null pointer exception. This was solved quite simply by making a small change to CST1b.juliet.setup.ElectionDeserializer
After successfully re-running GenerateElectionData we ran PrintPollCards. This program completed successfully. We inspected the letters that it had generated and noted some errors in the text of the letter:
CST1b.juliet.election.VoterMost of these changes were trivial.
Next we started the submission server and authentication server. There were no problems on startup but we did note that some difference in the arguments taken. Some coders had interpreted 'path' to mean the directory location and filename, while others had interpreted it as just meaning the directory location. We decided to make all the programs consistent, and that only the location was needed in the 'path' the filename of the election object was fixed as 'election' in the specification.
We started the client and were able to login, but we noted that there were problems in the repainting of the applet, that required the browser window to be resized before the applet repainted. During correcting this problem we also decided it might be an idea to change the login from a panel to a dialog box.
The client seemed to accept votes fine itself, but didn't actually seem to connect to the authentication server. Further inspection of the code found that this was because the line of code to actually make the connection was missing! This was fixed, but the client was still trying to connect to 'null' rather than the specified authentication sever location. To solve this we modified CST1b.juliet.client.VoteCaster to use sockets rather than URLs as URLs in Java did not support port numbers.
There were also problems in the authentication protocol. We had changed the protocol from sending character streams to String objects. This mean that we no longer required newlines at the end of a string, we modified the authentication server accordingly, and also made some forgotten modifications in CST1b.juliet.client.VoteCaster The authentication server also seemed to be sending a spurious "Votes saved" response which was not in the specification. This was removed and an "OK" response was added after the Retrieve stage, as in the spec.
While making these modifications the class CST1b.juliet.client.UserAuthenticator was merged into CST1b.juliet.client.VoteCaster Also, the class CST1b.juliet.ElectionGrabber was modified so that it no longer cached the election object. This was not necessary.
We tried to use the client again. This time it succeeded in connecting the Authentication Server, and the server sent signed sets of votes back to the client.
The client was unable to correctly unBlind the signature because of header information in the serialised object. We were resigned to stop using serialisation in this respect and make many changes. A new interfaceCST1b.juliet.security.Signable was created. CST1b.juliet.security.SignedObject, CST1b.juliet.security.BlindObject and CST1b.juliet.election.Vote were modified to make use of this interface. More tests were devised and carried out for SignedObject. Small changes were made to the implementation of the algorithms in CST1b.juliet.security.BlindObject and CST1b.juliet.security.SignedObject. CST1b.juliet.client.VoteCaster was modified to reflect the new handling of the BlindSignature. Also it was discovered that the VoteCaster was inserting the real vote in one set of votes rather than in each set of votes. This was fixed.
Initially the authentication server had been written so that it only coped with votes for one position. While we were modifying the server so that it coped with multiple positions, we noted that our protocol did not allow the client to specify which position's votes it wanted to retrieve. Accordingly we modified the protocol. After the client bents the "RETRIEVE" message it will send another string object containing the Position ID of the position whose votes it want to retrieve.
The single position election that we originally attempted to carry out was rerun (with amended poll dates) and was carried out successfully. We did note that the it was slightly difficult to read off the elected candidates from the generated HTML files because of all the other information in the count control form, and decided that we should also generate a simple elected candidates list at the top of each of the results pages. Also we decided that OutputResults should automatically generate a StyleSheet if none had been placed in the public_html directory.
We next decided to carry out a larger election, similar to that which we had described in our Phase 3 Testing Description. The details of the election that we set up were:
Election Title: ElectionName-TestWe used the following voterfile of ten voters and selected all ten to take part in the election:
Ian Bunning, IanB, Trinity Hall\nCambridge Ian Campbell, IanC, Churchill College\nCambridge Tak Fung, Tak, Girton College\nCambridge Ritchie Hughes, Ritchie, Pembroke College\nCambridge Sagar Shah, Sags, Sidney Sussex College\nCambridge Will Smith, WillSmith, USA Spiderman, Spiderman, America King Tut, KingTut, Eygpt Akira, Akira, Japan Ronaldo, Ronaldo, Brazil
After using the Setup GUI to set all these parameters we ran GenerateElectionData. It did not appear to be creating directories for each position (named by the positionID) under the election/signed_votes directory. We found that it was attempting to create these directories before the actually loading the election object. This was fixed trivially.
We re-ran GenerateElectionData and then proceeded to run PrintPollCards. On inspecting the generated poll cards we discovered that the voter's address was still not being printed. We inspected the PrintPollCards class and found nothing wrong or missing. We traced the problem back to the Setup GUI, it was not correctly reading the voters' addresses from the comma separated voterlist file. We corrected this (again only a tiny change to the code) and reran the Setup, GenerateElectionData and PrintPollCards.
We printed the PollCards to screen (they could have also easily been printed to paper) and noted the PAKs for each voter.
Next we exported a Client.html file (within which the client applet will run) to a public_html directory and also copied over the classes that the applet would use.
We started the two Servers and loaded the Applet into a web browser. As we started to use the client we noticed that it did not display the ballot paper of the second position when we clicked on the appropriate button. We investigated this problem in the VotingBoothPanel class. After doing lots of checking and tracing trying to find the problem we concluded that the adding/removing or showing/hiding of ballot papers from button clicks would not do what we wished. We modified the class to use a CardLayout and then used the methods in the java.awt.CardLayout class to switch between the various BallotPapers as and when required.
After correcting the client we proceeded to use it to make votes. All went as it should. The communication with the servers was now fine, the votes were authenticated by the authentication server and accepted by the submission server. We also tried to enter some invalid votes, using incorrect userIDs and PAKs, these were rejected as they should have been. We also tried to vote more than once under some userIDs. The system did not allow this, just as it shouldn't.
After all the votes had been cast we stopped the servers, and started the STV class. There were no apparent problems here. We next ran the OutputResults class and inspected the generated Results file. The results for the position "President" were identical to our manual run of the election that we had carried out before starting the computer one. The results for the position "Lackey" were not. We noted that the generated results file showed that there was only one space for Lackey when there should have been three (as we entered in the Setup GUI). We did not notice this problem with the previous smaller election that we had run because this only included one space anyway.
After checking and convincing ourselves that there was nothing wrong with the STV classes and OutputResults we traced the problem all the way back to the SetupGUI itself. The Setup GUI was designed so that if it could not correctly parse an integer from the string (representing the text in the textbox) then it would automatically set the spaces variable to 1 for that particular position. Although an error message was sent out to the console there was a lot of debugging information so it wasn't noticed earlier. We found that the Setup GUI was calling the getText method on the wrong textbox when it was initialising the string for startspaces. This is why the parseInt failed.
We fixed the Setup GUI and got the STV module to use the corrected election object with the previously cast votes. We inspected the output generated by OutputResults. This time the results for both positions matched the manual election that had been carried out.
Next we deleted all details of that election and re-ran it from Setup to OutputResults. We experienced no problems along the way
We also decided that it was necessary to do some Load/Stress Testing. The analyst (IanC) wrote a StressTest class which creates an election with a configurable number of positions, candidates and spaces with a configurable number of voters. It then submits votes for all of those voters and runs the tallying phase. We ran a test of 2500 voters, with 32 positions. Each position having a varying number of candidates and spaces (with no.spaces<no.candidates).
From the stress tests we did discover that the java implementation was not cleaning up threads properly, and so we would recommend that people start the servers passing the -green option to java.
Our acceptance criteria for this project were:
We believe that our project has been successful and that we have met the acceptance criteria:
CST1b.juliet.security.EncryptedInputFilter and CST1b.juliet.security.EncryptedOutputFilter which could be rewritten to implement this feature. Similarly our system does not produce Private and Public Keys using a secure methods but it does reference classes such as SecureRandom, PublicKey and PrivateKey, that could be rewritten to do so.CST1b.juliet| Class Name | Author |
GenerateElectionData |
Ritchie |
OutputResults |
Tak |
PrintPollCards |
Ritchie |
Setup |
Ritchie |
STV |
Tak |
SubmissionServer |
IanC |
AuthenticationServer |
IanB |
CST1b.juliet.authserver| Class Name | Author |
AuthThread |
IanB |
UserData |
IanB |
CST1b.juliet.client| Class Name | Author |
BallotPaper |
Ritchie |
BallotPaperSpoiltException |
Ritchie |
Client |
Ritchie |
ElectionGrabber |
Ritchie |
ElectionNotGrabbedException |
Ritchie |
LoginPanel |
Ritchie |
UserAuthenticationException |
Ritchie |
VoteCaster |
Ritchie |
VoteNotCastException |
Ritchie |
VoteNotSignedException |
Ritchie |
VotingBoothPanel |
Ritchie |
CST1b.juliet.election| Class Name | Author |
Candidate |
IanC |
Election |
IanB & IanC |
ElectionDataGenerator |
Ritchie |
IDNotUniqueException |
IanC |
NoMorePreferencesException |
IanC |
Position |
IanC |
Vote |
IanC |
Voter |
IanB |
CST1b.juliet.security| Class Name | Author |
BlindFactorTooSmallException |
IanC |
BlindObject |
IanB & IanC |
EncryptedInputFilter |
IanB |
EncryptedOutputFilter |
IanB |
Key |
IanB |
KeyPair |
IanB |
PrivateKey |
IanB |
PublicKey |
IanB |
SecureRandom |
IanB |
Signable [interface not class] |
IanC |
SignedObject |
IanB & IanC |
CST1b.juliet.setup| Class Name | Author |
ElectionDeserializer |
Ritchie |
ElectionSerializer |
Ritchie |
KeySerializer |
Ritchie |
VotersDeserializer |
Ritchie |
VotersSerializer |
Ritchie |
CST1b.juliet.submission| Class Name | Author |
Server |
IanC |
CST1b.juliet.tally| Class Name | Author |
CandVoteRec |
Tak |
CountControlForm |
Tak |
IDNotFoundException |
Tak |
STVModule |
Tak |
To facilitate testing, the following classes have been written by IanC:
Test.javaTestHarness.javaTestHarnessGUI.javaTestHarnessText.javaTestTest.javaUntestableTest.javaThese classes reside in the package CST1b.juliet.testharness. All major classes have an associated [ClassName]Test.java Class that will be used by the Testharness in performing tests.
The exception classes are trivial and did not require testing above checking that they compiled correctly. The test classes for exceptions and other untestable classes subclass Untestable Test.
The vast majority of the technical documentation consists of Javadoc output available on the web at http://www.thor.cam.ac.uk/group/CST1b/juliet/javadoc/. For completeness we also include here a documentation of the communications protocol between the client and servers, and also details of the STV Counting Regulations that our system implements.
The following is a comprehensive account of how counting of the votes occur within this computer system. It is based upon Points 5 to 17 of the Single Transferable Vote Regulations in the University of Cambridge Statutes and Ordinances, pages 121 to 124. This account has been designed firstly to take into account the differences between counting votes on a computer and counting by hand, and secondly to present the rules of counting in a clearer form of English.
1.1 Count all the voting papers to determine the total number of votes cast(=total valid vote).
Quota = (Total Valid Votes) / (number of VACANCIES to be filled+1)
if (quota>100) quota=ceiling(quota);
else quota=(quota round UP in the second d.p.)
1.5 Considering each candidate in turn in DESCENDING order of their votes, deem elected any candidate whose vote equals or exceeds the quota, up to the number of places to be filled, subject to NOTES (*).
1.6 That completes the first stage of the count.