From a31d88bd128debaf38734380b2b95eef6bef0394 Mon Sep 17 00:00:00 2001 From: Arbel Deutsch Peled Date: Sat, 5 Dec 2015 14:25:02 +0200 Subject: [PATCH] First implementation of simple BB Client --- .../SimpleBulletinBoardClient.java | 163 ++++++++++++++++++ ...tinBoard.java => BulletinBoardClient.java} | 20 ++- settings.gradle | 2 + 3 files changed, 178 insertions(+), 7 deletions(-) create mode 100644 bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java rename meerkat-common/src/main/java/meerkat/bulletinboard/{BulletinBoard.java => BulletinBoardClient.java} (67%) diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java new file mode 100644 index 0000000..aa61726 --- /dev/null +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java @@ -0,0 +1,163 @@ +package meerkat.bulletinboard; + +import com.google.protobuf.ByteString; +import meerkat.comm.CommunicationException; +import meerkat.crypto.Digest; +import meerkat.crypto.concrete.SHA256Digest; +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.rest.*; + +import java.util.List; + +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; + +/** + * Created by Arbel Deutsch Peled on 05-Dec-15. + */ +public class SimpleBulletinBoardClient implements BulletinBoardClient { + + //TODO: Make this general + private static String SQL_SERVER_POST = "sqlserver/postmessage"; + private static String SQL_SERVER_GET = "sqlserver/readmessages"; + + private List meerkatDBs; + + private Client client; + + private Digest digest; + + /** + * Stores database locations and initializes the web Client + * @param meerkatDBs is the list of database locations + */ + @Override + public void init(List meerkatDBs) { + + this.meerkatDBs = meerkatDBs; + + client = ClientBuilder.newClient(); + client.register(ProtobufMessageBodyReader.class); + client.register(ProtobufMessageBodyWriter.class); + + digest = new SHA256Digest(); + + } + + /** + * Post message to all DBs + * Make only one try per DB. + * @param msg is the message, + * @return the message ID for later retrieval + * @throws CommunicationException + */ + @Override + public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { + + WebTarget webTarget; + Response response; + + // Post message to all databases + try { + for (String db : meerkatDBs) { + webTarget = client.target(db).path(SQL_SERVER_POST); + response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF)); + + // Only consider valid responses + if (response.getStatusInfo() == Response.Status.OK + || response.getStatusInfo() == Response.Status.CREATED) { + response.readEntity(BoolMsg.class).getValue(); + } + } + } catch (Exception e) { // Occurs only when server replies with valid status but invalid data + throw new CommunicationException("Error accessing database: " + e.getMessage()); + } + + // Calculate the correct message ID and return it + digest.reset(); + digest.update(msg.getMsg()); + return MessageID.newBuilder().setID(ByteString.copyFrom(digest.digest())).build(); + } + + /** + * Access each database and search for a given message ID + * Return the number of databases in which the message was found + * Only try once per DB + * Ignore communication exceptions in specific databases + * @param id is the requested message ID + * @return the number of DBs in which retrieval was successful + */ + @Override + public float getRedundancy(MessageID id) { + WebTarget webTarget; + Response response; + + MessageFilterList filterList = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.MSG_ID) + .setId(id.getID()) + .build()) + .build(); + + int count = 0; + + for (String db : meerkatDBs) { + try { + webTarget = client.target(db).path(SQL_SERVER_GET); + + response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF)); + + if (response.readEntity(BulletinBoardMessageList.class).getMessageCount() > 0){ + count++; + } + + } catch (Exception e) {} + } + + return count; + } + + /** + * Go through the DBs and try to retrieve messages according to the specified filter + * If at the operation is successful for some DB: return the results and stop iterating + * If no operation is successful: return null (NOT blank list) + * @param filterList return only messages that match the filters (null means no filtering). + * @return + */ + @Override + public List readMessages(MessageFilterList filterList) { + WebTarget webTarget; + Response response; + BulletinBoardMessageList messageList; + + // Replace null filter list with blank one. + if (filterList == null){ + filterList = MessageFilterList.newBuilder().build(); + } + + for (String db : meerkatDBs) { + try { + webTarget = client.target(db).path(SQL_SERVER_GET); + + response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF)); + + messageList =response.readEntity(BulletinBoardMessageList.class); + + if (messageList != null){ + return messageList.getMessageList(); + } + + } catch (Exception e) {} + } + + return null; + } + + @Override + public void registerNewMessageCallback(MessageCallback callback, MessageFilterList filterList) { + callback.handleNewMessage(readMessages(filterList)); + } +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoard.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java similarity index 67% rename from meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoard.java rename to meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java index 0efd6a7..2e466b3 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoard.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java @@ -8,7 +8,14 @@ import java.util.List; /** * Created by talm on 24/10/15. */ -public interface BulletinBoard { +public interface BulletinBoardClient { + + /** + * Initialize the client to use some specified servers + * @param meerkatDBs is the list of database locations + */ + void init(List meerkatDBs); + /** * Post a message to the bulletin board * @param msg @@ -27,22 +34,21 @@ public interface BulletinBoard { * Note that if messages haven't been "fully posted", this might return a different * set of messages in different calls. However, messages that are fully posted * are guaranteed to be included. - * @param filter return only messages that match the filter (null means no filtering). - * @param max maximum number of messages to return (0=no limit) + * @param filterList return only messages that match the filters (null means no filtering). * @return */ - List readMessages(MessageFilter filter, int max); + List readMessages(MessageFilterList filterList); interface MessageCallback { - void handleNewMessage(BulletinBoardMessage msg); + void handleNewMessage(List msg); } /** * Register a callback that will be called with each new message that is posted. * The callback will be called only once for each message. * @param callback - * @param filter only call back for messages that match the filter. + * @param filterList only call back for messages that match the filter. */ - void registerNewMessageCallback(MessageCallback callback, MessageFilter filter); + void registerNewMessageCallback(MessageCallback callback, MessageFilterList filterList); } diff --git a/settings.gradle b/settings.gradle index e4ef054..99f4c5e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,3 +3,5 @@ include 'voting-booth' include 'bulletin-board-server' include 'polling-station' include 'restful-api-common' +include 'bulletin-board-client' +