polling station server and scanner client communicate (but missing response)

android-scanner
Laura Radaelli 2017-03-20 18:03:08 +02:00
parent 11b86dd2aa
commit 09ee63eca4
14 changed files with 486 additions and 17 deletions

View File

@ -0,0 +1,27 @@
package meerkat.pollingstation;
import com.google.protobuf.ByteString;
import meerkat.protobuf.PollingStation;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
/**
* Created by Laura on 3/20/2017.
*/
public class PollingStationClientToyRun {
private static final String ADDRESS = "http://localhost";
private static final String SUB_ADDRESS = "";
private static final int PORT = 8080;
public static void main(String [] args) {
byte[] data = {(byte) 1, (byte) 2};
PollingStation.ScannedData scannedData = PollingStation.ScannedData.newBuilder()
.setChannel(ByteString.copyFrom(data))
.build();
ScannerClientAPI scannerClient = new ScannerClientAPI(ADDRESS, SUB_ADDRESS, PORT, POLLING_STATION_WEB_SCANNER_SCAN_PATH);
scannerClient.sendScan(scannedData);
}
}

View File

@ -0,0 +1,82 @@
package meerkat.pollingstation;
import com.google.common.util.concurrent.FutureCallback;
import meerkat.pollingstation.controller.commands.PollingStationCommand;
import meerkat.pollingstation.controller.commands.ReceivedScanCommand;
import meerkat.protobuf.PollingStation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Created by Laura on 3/20/2017.
*/
public class PollingStationMainController {
private final Logger logger = LoggerFactory.getLogger(PollingStationMainController.class);
private LinkedBlockingQueue<PollingStationCommand> queue;
private volatile boolean shutDownHasBeenCalled;
public PollingStationMainController() {
queue = new LinkedBlockingQueue<>();
shutDownHasBeenCalled = false;
}
public void start() throws Exception {
while (! wasShutDownCalled()) {
try {
PollingStationCommand command = queue.take();
handleSingleCommand(command);
}
catch (InterruptedException e) {
System.err.println("Interrupted while reading from command queue " + e);
}
}
}
private boolean wasShutDownCalled () {
return shutDownHasBeenCalled;
}
/**
* 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(PollingStationCommand 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 = "handleSingleCommand: received a task too old. " +
// command.getBallotSerialNumber() + " " + state.currentBallotSerialNumber;
// logger.debug(errorMessage);
// return;
// }
// decide which method to run according to the command type
if (command instanceof ReceivedScanCommand) {
doProcessScan ();
}
// else if (command instanceof ChannelChoiceCommand) {
// doChooseChannel();
// }
else {
logger.error("handleSingleCommand: unknown type of PollingStationCommand received: " + command.getClass().getName());
// doReportErrorAndForceRestart(systemMessages.get(StorageManager.SOMETHING_WRONG_MESSAGE));
doReportErrorAndForceRestart("error message to define");
}
}
private void doProcessScan() {
}
/**
* a (overloaded) method to report an error message to the voter
* @param errorMessage message to show the voter
*/
private void doReportErrorAndForceRestart(String errorMessage) {
queue.clear();
}
}

View File

@ -0,0 +1,26 @@
package meerkat.pollingstation;
/**
* Created by Laura on 3/20/2017.
*/
public class PollingStationToyRun {
private static PollingStationScanner.Consumer scanner;
private static final String ADDRESS = "http://localhost";
private static final String SUB_ADDRESS = "";
private static final int PORT = 8080;
public static void main(String [] args) {
System.err.println("Setting up Scanner WebApp!");
scanner = new ReceiverScanHandler(PORT, SUB_ADDRESS);
try {
scanner.start();
} catch (Exception e) {
System.err.println("Could not start server: " + e.getMessage());
}
}
}

View File

@ -19,7 +19,7 @@ import meerkat.rest.*;
public class PollingStationWebScanner implements PollingStationScanner.Consumer{
public final static String CALLBACKS_ATTRIBUTE_NAME = "callbacks";
public final static String CALLBACKS_ATTRIBUTE_NAME = "controller";
private final Server server;
private final List<FutureCallback<ScannedData>> callbacks;

View File

@ -1,6 +1,8 @@
package meerkat.pollingstation;
import com.google.common.util.concurrent.FutureCallback;
import meerkat.pollingstation.controller.callbacks.ScanCallback;
import meerkat.pollingstation.controller.callbacks.ScanDataCallback;
import meerkat.protobuf.PollingStation;
import meerkat.rest.ProtobufMessageBodyReader;
import meerkat.rest.ProtobufMessageBodyWriter;
@ -12,25 +14,33 @@ import org.glassfish.jersey.servlet.ServletContainer;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Created by Laura on 3/20/2017.
*/
public class ReceiverScanHandler implements PollingStationScanner.Consumer{
public final static String CALLBACKS_ATTRIBUTE_NAME = "callbacks";
public final static String CALLBACKS_ATTRIBUTE_NAME = "controller";
private final Server server;
private final List<FutureCallback<PollingStation.ScannedData>> callbacks;
private LinkedBlockingQueue<ScanCallback> queue;
private volatile boolean shutDownHasBeenCalled;
public ReceiverScanHandler(int port, String subAddress) {
callbacks = new LinkedList<>();
server = new Server(port);
queue = new LinkedBlockingQueue<>();
shutDownHasBeenCalled = false;
ServletContextHandler servletContextHandler = new ServletContextHandler(server, subAddress);
servletContextHandler.setAttribute(CALLBACKS_ATTRIBUTE_NAME, (Iterable<FutureCallback<PollingStation.ScannedData>>) callbacks);
servletContextHandler.setAttribute(CALLBACKS_ATTRIBUTE_NAME, (LinkedBlockingQueue<ScanCallback>) queue);
ResourceConfig resourceConfig = new ResourceConfig(ReceiverWebAPI.class);
resourceConfig.register(ProtobufMessageBodyReader.class);
@ -39,11 +49,30 @@ public class ReceiverScanHandler implements PollingStationScanner.Consumer{
ServletHolder servletHolder = new ServletHolder(new ServletContainer(resourceConfig));
servletContextHandler.addServlet(servletHolder, "/*");
}
private boolean wasShutDownCalled () {
return shutDownHasBeenCalled;
}
@Override
public void start() throws Exception {
server.start();
while (! wasShutDownCalled()) {
try {
ScanDataCallback callback = (ScanDataCallback) queue.take();
// handleSingleCommand(Command);
callback.doNothing();
}
catch (InterruptedException e) {
System.err.println("Interrupted while reading from command queue " + e);
}
}
}
@Override

View File

@ -2,6 +2,7 @@ package meerkat.pollingstation;
import com.google.common.util.concurrent.FutureCallback;
import com.google.protobuf.BoolValue;
import meerkat.pollingstation.controller.callbacks.ScanDataCallback;
import meerkat.protobuf.PollingStation;
import javax.annotation.PostConstruct;
@ -11,7 +12,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
@ -19,7 +20,7 @@ import static meerkat.rest.Constants.MEDIATYPE_PROTOBUF;
/**
* Implements a Web-App interface for {@link meerkat.pollingstation.PollingStationScanner.Producer}
* This class depends on {@link meerkat.pollingstation.PollingStationWebScanner} and works in conjunction with it
* This class depends on {@link meerkat.pollingstation.ReceiverScanHandler} and works in conjunction with it
*/
@Path("/")
public class ReceiverWebAPI implements PollingStationScanner.Producer {
@ -27,7 +28,7 @@ public class ReceiverWebAPI implements PollingStationScanner.Producer {
@Context
ServletContext servletContext;
Iterable<FutureCallback<PollingStation.ScannedData>> callbacks;
LinkedBlockingQueue<FutureCallback<PollingStation.ScannedData>> callbacks;
/**
* This method is called by the Jetty engine when instantiating the servlet
@ -35,10 +36,10 @@ public class ReceiverWebAPI implements PollingStationScanner.Producer {
@PostConstruct
@SuppressWarnings("unchecked")
public void init() throws Exception {
Object context = servletContext.getAttribute(PollingStationWebScanner.CALLBACKS_ATTRIBUTE_NAME);
Object context = servletContext.getAttribute(ReceiverScanHandler.CALLBACKS_ATTRIBUTE_NAME);
try {
callbacks = (Iterable<FutureCallback<PollingStation.ScannedData>>) context;
callbacks = (LinkedBlockingQueue<FutureCallback<PollingStation.ScannedData>>) context;
} catch (ClassCastException e) {
throw e;
}
@ -54,12 +55,17 @@ public class ReceiverWebAPI implements PollingStationScanner.Producer {
boolean handled = false;
for (FutureCallback<PollingStation.ScannedData> callback : callbacks){
// for (FutureCallback<PollingStation.ScannedData> callback : callbacks){
//
// callback.onSuccess(scannedData);
// handled = true;
//
// }
callback.onSuccess(scannedData);
handled = true;
ScanDataCallback callback = new ScanDataCallback(scannedData);
// scanner.subscribe(callback);
callbacks.add(callback);
}
return BoolValue.newBuilder()
.setValue(handled)
@ -76,12 +82,16 @@ public class ReceiverWebAPI implements PollingStationScanner.Producer {
boolean handled = false;
for (FutureCallback<PollingStation.ScannedData> callback : callbacks){
// for (FutureCallback<PollingStation.ScannedData> callback : callbacks){
//
// callback.onFailure(new IOException(errorMsg.getMsg()));
// handled = true;
//
// }
callback.onFailure(new IOException(errorMsg.getMsg()));
handled = true;
}
ScanDataCallback callback = new ScanDataCallback(errorMsg);
// scanner.subscribe(callback);
callbacks.add(callback);
return BoolValue.newBuilder()
.setValue(handled)

View File

@ -1,7 +1,45 @@
package meerkat.pollingstation;
import com.google.protobuf.BoolValue;
import meerkat.protobuf.BulletinBoardAPI;
import meerkat.protobuf.PollingStation;
import meerkat.rest.Constants;
import meerkat.rest.ProtobufMessageBodyReader;
import meerkat.rest.ProtobufMessageBodyWriter;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
/**
* Created by Laura on 3/20/2017.
*/
public class ScannerClientAPI {
private Client client;
private WebTarget webTarget;
public ScannerClientAPI(String address, String sub_address, int port, String path) {
client = ClientBuilder.newClient();
client.register(ProtobufMessageBodyReader.class);
client.register(ProtobufMessageBodyWriter.class);
webTarget = client.target(address + ":" + port)
.path(sub_address).path(path);
}
public void sendScan(PollingStation.ScannedData scannedData) {
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(scannedData, Constants.MEDIATYPE_PROTOBUF));
BoolValue res = response.readEntity(BoolValue.class);
System.out.println(res.toString());
response.close();
}
public void sendError(PollingStation.ErrorMsg errorMsg) {
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(errorMsg, Constants.MEDIATYPE_PROTOBUF));
response.close();
}
}

View File

@ -0,0 +1,8 @@
package meerkat.pollingstation.controller.callbacks;
/**
* Created by Laura on 3/20/2017.
*/
public abstract class ScanCallback {
}

View File

@ -0,0 +1,40 @@
package meerkat.pollingstation.controller.callbacks;
import com.google.common.util.concurrent.FutureCallback;
import meerkat.protobuf.PollingStation;
/**
* Created by Laura on 3/20/2017.
*/
public class ScanDataCallback extends ScanCallback implements FutureCallback<PollingStation.ScannedData> {
private final PollingStation.ScannedData expectedData;
private final PollingStation.ErrorMsg errorMsg;
private boolean dataIsAsExpected;
private Throwable thrown;
public ScanDataCallback(PollingStation.ScannedData expectedData) {
this.expectedData = expectedData;
this.errorMsg = null;
}
public ScanDataCallback(PollingStation.ErrorMsg errorMsg) {
this.expectedData = null;
this.errorMsg = errorMsg;
}
@Override
public void onSuccess(PollingStation.ScannedData result) {
dataIsAsExpected = result.getChannel().equals(expectedData.getChannel());
}
@Override
public void onFailure(Throwable t) {
dataIsAsExpected = false;
thrown = t;
}
public void doNothing() {
System.out.println("do nothing hit");
}
}

View File

@ -0,0 +1,28 @@
package meerkat.pollingstation.controller.callbacks;
import com.google.common.util.concurrent.FutureCallback;
import meerkat.protobuf.PollingStation;
/**
* Created by Laura on 3/20/2017.
*/
public class ScanErrorCallback extends ScanCallback implements FutureCallback<PollingStation.ErrorMsg> {
private final String expectedErrorMessage;
public ScanErrorCallback(String expectedErrorMessage) {
this.expectedErrorMessage = expectedErrorMessage;
}
@Override
public void onSuccess(PollingStation.ErrorMsg msg) {
// dataIsAsExpected = false;
// semaphore.release();
}
@Override
public void onFailure(Throwable t) {
// dataIsAsExpected = t.getMessage().equals(expectedErrorMessage);
// semaphore.release();
}
}

View File

@ -0,0 +1,22 @@
package meerkat.pollingstation.controller.commands;
/**
* Created by Laura on 3/20/2017.
*/
public abstract class PollingStationCommand {
protected final int requestIdentifier;
protected final long ballotSerialNumber;
protected PollingStationCommand(int requestIdentifier, long ballotSerialNumber) {
this.requestIdentifier = requestIdentifier;
this.ballotSerialNumber = ballotSerialNumber;
}
public long getBallotSerialNumber () {
return this.ballotSerialNumber;
}
public int getRequestIdentifier () {
return this.requestIdentifier;
}
}

View File

@ -0,0 +1,10 @@
package meerkat.pollingstation.controller.commands;
/**
* Created by Laura on 3/20/2017.
*/
public class ReceivedScanCommand extends PollingStationCommand {
public ReceivedScanCommand(int requestIdentifier, long ballotSerialNumber) {
super(requestIdentifier, ballotSerialNumber);
}
}

View File

@ -0,0 +1,148 @@
package meerkat.pollingstation;
import com.google.common.util.concurrent.FutureCallback;
import com.google.protobuf.ByteString;
import meerkat.protobuf.PollingStation.ErrorMsg;
import meerkat.protobuf.PollingStation.ScannedData;
import meerkat.rest.Constants;
import meerkat.rest.ProtobufMessageBodyReader;
import meerkat.rest.ProtobufMessageBodyWriter;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import java.util.concurrent.Semaphore;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
import static org.hamcrest.MatcherAssert.assertThat;
public class Receiver_ClientTest {
private PollingStationScanner.Consumer scanner;
private static final String ADDRESS = "http://localhost";
private static final String SUB_ADDRESS = "";
private static final int PORT = 8080;
private Semaphore semaphore;
private Throwable thrown;
private boolean dataIsAsExpected;
private class ScanHandler implements FutureCallback<ScannedData> {
private final ScannedData expectedData;
public ScanHandler(ScannedData expectedData) {
this.expectedData = expectedData;
}
@Override
public void onSuccess(ScannedData result) {
dataIsAsExpected = result.getChannel().equals(expectedData.getChannel());
semaphore.release();
}
@Override
public void onFailure(Throwable t) {
dataIsAsExpected = false;
thrown = t;
semaphore.release();
}
}
private class ErrorHandler implements FutureCallback<ScannedData> {
private final String expectedErrorMessage;
public ErrorHandler(String expectedErrorMessage) {
this.expectedErrorMessage = expectedErrorMessage;
}
@Override
public void onSuccess(ScannedData result) {
dataIsAsExpected = false;
semaphore.release();
}
@Override
public void onFailure(Throwable t) {
dataIsAsExpected = t.getMessage().equals(expectedErrorMessage);
semaphore.release();
}
}
@Before
public void init() {
System.err.println("Setting up Scanner WebApp!");
scanner = new ReceiverScanHandler(PORT, SUB_ADDRESS);
semaphore = new Semaphore(0);
thrown = null;
try {
scanner.start();
} catch (Exception e) {
assertThat("Could not start server: " + e.getMessage(), false);
}
}
@Test
public void testSuccessfulScan() throws InterruptedException {
byte[] data = {(byte) 1, (byte) 2};
ScannedData scannedData = ScannedData.newBuilder()
.setChannel(ByteString.copyFrom(data))
.build();
scanner.subscribe(new ScanHandler(scannedData));
ScannerClientAPI scannerClient = new ScannerClientAPI(ADDRESS, SUB_ADDRESS, PORT, POLLING_STATION_WEB_SCANNER_SCAN_PATH);
scannerClient.sendScan(scannedData);
semaphore.acquire();
assertThat("Scanner has thrown an error", thrown == null);
assertThat("Scanned data received was incorrect", dataIsAsExpected);
}
@Test
public void testErroneousScan() throws InterruptedException {
ErrorMsg errorMsg = ErrorMsg.newBuilder()
.setMsg("!Error Message!")
.build();
scanner.subscribe(new ErrorHandler(errorMsg.getMsg()));
ScannerClientAPI scannerClient = new ScannerClientAPI(ADDRESS, SUB_ADDRESS, PORT, POLLING_STATION_WEB_SCANNER_ERROR_PATH);
scannerClient.sendError(errorMsg);
semaphore.acquire();
assertThat("Scanner error received was incorrect", dataIsAsExpected);
}
@After
public void close() {
System.err.println("ReceiverScanHandler shutting down...");
try {
scanner.stop();
} catch (Exception e) {
assertThat("Could not stop server: " + e.getMessage(), false);
}
}
}

View File

@ -34,6 +34,7 @@ public class ProtobufMessageBodyWriter implements MessageBodyWriter<Message> {
public void writeTo(Message message, Class<?> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
throws IOException, WebApplicationException {
System.out.println("protobufmessagebodywriter");
message.writeTo(entityStream);
}
}