From c806e7b32a6ffba5f08cb7ff0fffe8bb78051a16 Mon Sep 17 00:00:00 2001 From: Arbel Deutsch Peled Date: Wed, 13 Apr 2016 09:46:24 +0300 Subject: [PATCH 01/15] Added Deletion to Bulletin Board Server and Local Client --- .../CachedBulletinBoardClient.java | 312 ++++++++++++++---- .../LocalBulletinBoardClient.java | 36 +- .../SingleServerBulletinBoardClient.java | 4 +- .../ThreadedBulletinBoardSubscriber.java | 9 +- .../GenericSubscriptionClientTester.java | 5 +- .../LocalBulletinBoardClientTest.java | 2 +- .../sqlserver/BulletinBoardSQLServer.java | 46 ++- .../sqlserver/H2QueryProvider.java | 53 ++- .../sqlserver/MySQLQueryProvider.java | 12 +- .../bulletinboard/BulletinBoardClient.java | 4 +- .../BulletinBoardMessageDeleter.java | 30 ++ .../DeletableBulletinBoardServer.java | 29 ++ ...etableSubscriptionBulletinBoardClient.java | 7 + .../SubscriptionAsyncBulletinBoardClient.java | 7 - .../SubscriptionBulletinBoardClient.java | 7 + 15 files changed, 448 insertions(+), 115 deletions(-) create mode 100644 meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardMessageDeleter.java create mode 100644 meerkat-common/src/main/java/meerkat/bulletinboard/DeletableBulletinBoardServer.java create mode 100644 meerkat-common/src/main/java/meerkat/bulletinboard/DeletableSubscriptionBulletinBoardClient.java delete mode 100644 meerkat-common/src/main/java/meerkat/bulletinboard/SubscriptionAsyncBulletinBoardClient.java create mode 100644 meerkat-common/src/main/java/meerkat/bulletinboard/SubscriptionBulletinBoardClient.java diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java index 96ba76d..6aac297 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java @@ -1,168 +1,338 @@ package meerkat.bulletinboard; import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.ListeningScheduledExecutorService; -import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.ByteString; import meerkat.comm.CommunicationException; -import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.protobuf.Voting.*; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.concurrent.Executors; /** * Created by Arbel Deutsch Peled on 03-Mar-16. * This is a full-fledged implementation of a Bulletin Board Client * It provides asynchronous access to several remote servers, as well as a local cache - * Read/write operations are performed on the local server + * Read operations are performed on the local server + * Batch reads are performed on the local server and, if they fail, also on the remote servers + * Write operations are performed first on the local server and then on the remotes * After any read is carried out, a subscription is made for the specific query to make sure the local DB will be updated * The database also employs a synchronizer which makes sure local data is sent to the remote servers */ -public class CachedBulletinBoardClient implements SubscriptionAsyncBulletinBoardClient { +public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClient { - private final BulletinBoardClient localClient; + private final AsyncBulletinBoardClient localClient; private AsyncBulletinBoardClient remoteClient; private BulletinBoardSubscriber subscriber; - private final int threadPoolSize; - private final long failDelayInMilliseconds; - private final long subscriptionIntervalInMilliseconds; + private class SubscriptionStoreCallback implements FutureCallback> { - public CachedBulletinBoardClient(BulletinBoardClient localClient, - int threadPoolSize, - long failDelayInMilliseconds, - long subscriptionIntervalInMilliseconds) - throws IllegalAccessException, InstantiationException { + private final FutureCallback callback; + + public SubscriptionStoreCallback(){ + callback = null; + } + + public SubscriptionStoreCallback(FutureCallback callback){ + this.callback = callback; + } + + @Override + public void onSuccess(List result) { + for (BulletinBoardMessage msg : result) { + try { + localClient.postMessage(msg); + } catch (CommunicationException ignored) { + // TODO: log + } + } + } + + @Override + public void onFailure(Throwable t) { + if (callback != null) { + callback.onFailure(t); // This is some hard error that cannot be dealt with + } + } + + } + + /** + * Creates a Cached Client + * Assumes all parameters are initialized + * @param localClient is a Client for the local instance + * @param remoteClient is a Client for the remote instance(s); Should have endless retries for post operations + * @param subscriber is a subscription service to the remote instance(s) + */ + public CachedBulletinBoardClient(AsyncBulletinBoardClient localClient, + AsyncBulletinBoardClient remoteClient, + BulletinBoardSubscriber subscriber) { this.localClient = localClient; - this.threadPoolSize = threadPoolSize; - this.failDelayInMilliseconds = failDelayInMilliseconds; - this.subscriptionIntervalInMilliseconds = subscriptionIntervalInMilliseconds; - - remoteClient = new ThreadedBulletinBoardClient(); + this.remoteClient = remoteClient; + this.subscriber = subscriber; } @Override - public MessageID postMessage(BulletinBoardMessage msg, FutureCallback callback) { - return null; - } + public MessageID postMessage(final BulletinBoardMessage msg, final FutureCallback callback) { - @Override - public MessageID postBatch(CompleteBatch completeBatch, FutureCallback callback) { - return null; - } + return localClient.postMessage(msg, new FutureCallback() { + @Override + public void onSuccess(Boolean result) { + remoteClient.postMessage(msg, callback); + } - @Override - public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback callback) { + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }); } @Override - public void postBatchData(byte[] signerId, int batchId, List batchDataList, int startPosition, FutureCallback callback) { + public MessageID postBatch(final CompleteBatch completeBatch, final FutureCallback callback) { + + return localClient.postBatch(completeBatch, new FutureCallback() { + @Override + public void onSuccess(Boolean result) { + remoteClient.postBatch(completeBatch, callback); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }); } @Override - public void postBatchData(byte[] signerId, int batchId, List batchDataList, FutureCallback callback) { + public void beginBatch(final BeginBatchMessage beginBatchMessage, final FutureCallback callback) { + + localClient.beginBatch(beginBatchMessage, new FutureCallback() { + @Override + public void onSuccess(Boolean result) { + remoteClient.beginBatch(beginBatchMessage, callback); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }); } @Override - public void postBatchData(ByteString signerId, int batchId, List batchDataList, int startPosition, FutureCallback callback) { + public void postBatchData(final byte[] signerId, final int batchId, final List batchDataList, + final int startPosition, final FutureCallback callback) { + + localClient.postBatchData(signerId, batchId, batchDataList, startPosition, new FutureCallback() { + @Override + public void onSuccess(Boolean result) { + remoteClient.postBatchData(signerId, batchId, batchDataList, startPosition, callback); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }); } @Override - public void postBatchData(ByteString signerId, int batchId, List batchDataList, FutureCallback callback) { + public void postBatchData(final byte[] signerId, final int batchId, final List batchDataList, final FutureCallback callback) { + + localClient.postBatchData(signerId, batchId, batchDataList, new FutureCallback() { + @Override + public void onSuccess(Boolean result) { + remoteClient.postBatchData(signerId, batchId, batchDataList, callback); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }); } @Override - public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback callback) { + public void postBatchData(final ByteString signerId, final int batchId, final List batchDataList, + final int startPosition, final FutureCallback callback) { + + localClient.postBatchData(signerId, batchId, batchDataList, startPosition, new FutureCallback() { + @Override + public void onSuccess(Boolean result) { + remoteClient.postBatchData(signerId, batchId, batchDataList, startPosition, callback); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }); + + } + + @Override + public void postBatchData(final ByteString signerId, final int batchId, final List batchDataList, final FutureCallback callback) { + + localClient.postBatchData(signerId, batchId, batchDataList, new FutureCallback() { + @Override + public void onSuccess(Boolean result) { + remoteClient.postBatchData(signerId, batchId, batchDataList, callback); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }); + + } + + @Override + public void closeBatch(final CloseBatchMessage closeBatchMessage, final FutureCallback callback) { + + localClient.closeBatch(closeBatchMessage, new FutureCallback() { + @Override + public void onSuccess(Boolean result) { + remoteClient.closeBatch(closeBatchMessage, callback); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }); } @Override public void getRedundancy(MessageID id, FutureCallback callback) { - } - - @Override - public void readMessages(MessageFilterList filterList, FutureCallback> callback) { + remoteClient.getRedundancy(id, callback); } @Override - public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback callback) { + public void readMessages(MessageFilterList filterList, final FutureCallback> callback) { + + localClient.readMessages(filterList, callback); + + subscriber.subscribe(filterList, new SubscriptionStoreCallback(callback)); + + } + + @Override + public void readBatch(final BatchSpecificationMessage batchSpecificationMessage, final FutureCallback callback) { + + localClient.readBatch(batchSpecificationMessage, new FutureCallback() { + @Override + public void onSuccess(CompleteBatch result) { + callback.onSuccess(result); // Read from local client was successful + } + + @Override + public void onFailure(Throwable t) { + + // Read from local unsuccessful: try to read from remote + + remoteClient.readBatch(batchSpecificationMessage, new FutureCallback() { + + @Override + public void onSuccess(CompleteBatch result) { + + // Read from remote was successful: store in local and return result + + localClient.postBatch(result, new FutureCallback() { + @Override + public void onSuccess(Boolean result) {} + @Override + public void onFailure(Throwable t) {} + }); + + callback.onSuccess(result); + + } + + @Override + public void onFailure(Throwable t) { + + // Read from remote was unsuccessful: report error + callback.onFailure(t); + + } + + }); + + } + + }); } @Override public void querySync(SyncQuery syncQuery, FutureCallback callback) { + localClient.querySync(syncQuery, callback); + } @Override - public void init(BulletinBoardClientParams clientParams) { - - remoteClient.init(clientParams); - - ListeningScheduledExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(threadPoolSize)); - - List subscriberClients = new ArrayList<>(clientParams.getBulletinBoardAddressCount()); - - for (String address : clientParams.getBulletinBoardAddressList()){ - - SubscriptionAsyncBulletinBoardClient newClient = - new SingleServerBulletinBoardClient(executorService, failDelayInMilliseconds, subscriptionIntervalInMilliseconds); - - newClient.init(clientParams.toBuilder().clearBulletinBoardAddress().addBulletinBoardAddress(address).build()); - - subscriberClients.add(newClient); - - } - - subscriber = new ThreadedBulletinBoardSubscriber(subscriberClients, localClient); - - } + /** + * This is a stub method + * All resources are assumed to be initialized + */ + public void init(BulletinBoardClientParams clientParams) {} @Override public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { - return null; + localClient.postMessage(msg); + return remoteClient.postMessage(msg); } @Override public float getRedundancy(MessageID id) { - return 0; + return remoteClient.getRedundancy(id); } @Override public List readMessages(MessageFilterList filterList) { - return null; + subscriber.subscribe(filterList, new SubscriptionStoreCallback()); + return localClient.readMessages(filterList); } @Override - public SyncQuery generateSyncQuery(GenerateSyncQueryParams GenerateSyncQueryParams) throws CommunicationException { - return null; + public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { + return localClient.generateSyncQuery(generateSyncQueryParams); } @Override public void close() { - + localClient.close(); + remoteClient.close(); } @Override public void subscribe(MessageFilterList filterList, FutureCallback> callback) { - + subscriber.subscribe(filterList, callback); } @Override public void subscribe(MessageFilterList filterList, long startEntry, FutureCallback> callback) { + subscriber.subscribe(filterList, startEntry, callback); + } + + public int syncStatus(){ + return 0; + } + + public void reSync(){ } -} + +} \ No newline at end of file diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java index df3e196..1f56690 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java @@ -22,14 +22,14 @@ import java.util.concurrent.TimeUnit; /** * Created by Arbel Deutsch Peled on 15-Mar-16. - * This client is to be used mainly for testing. - * It wraps a BulletinBoardServer in an asynchronous client. + * This client wraps a BulletinBoardServer in an asynchronous client. + * It is meant to be used as a local cache handler and for testing purposes. * This means the access to the server is direct (via method calls) instead of through a TCP connection. * The client implements both synchronous and asynchronous method calls, but calls to the server itself are performed synchronously. */ -public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardClient{ +public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBoardClient { - private final BulletinBoardServer server; + private final DeletableBulletinBoardServer server; private final ListeningScheduledExecutorService executorService; private final BatchDigest digest; private final int subsrciptionDelay; @@ -40,7 +40,7 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC * @param threadNum is the number of concurrent threads to allocate for the client * @param subscriptionDelay is the required delay between subscription calls in milliseconds */ - public LocalBulletinBoardClient(BulletinBoardServer server, int threadNum, int subscriptionDelay) { + public LocalBulletinBoardClient(DeletableBulletinBoardServer server, int threadNum, int subscriptionDelay) { this.server = server; this.executorService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(threadNum)); this.digest = new GenericBatchDigest(new SHA256Digest()); @@ -517,8 +517,30 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC } @Override - public SyncQuery generateSyncQuery(GenerateSyncQueryParams GenerateSyncQueryParams) throws CommunicationException { - return server.generateSyncQuery(GenerateSyncQueryParams); + public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { + return server.generateSyncQuery(generateSyncQueryParams); + } + + @Override + public void deleteMessage(MessageID msgID, FutureCallback callback) { + + try { + callback.onSuccess(server.deleteMessage(msgID).getValue()); + } catch (CommunicationException e) { + callback.onFailure(e); + } + + } + + @Override + public void deleteMessage(long entryNum, FutureCallback callback) { + + try { + callback.onSuccess(server.deleteMessage(entryNum).getValue()); + } catch (CommunicationException e) { + callback.onFailure(e); + } + } @Override diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java index 34531cf..f12430d 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java @@ -7,12 +7,10 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.ByteString; import meerkat.bulletinboard.workers.singleserver.*; import meerkat.comm.CommunicationException; -import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.protobuf.Voting.BulletinBoardClientParams; import meerkat.util.BulletinBoardUtils; -import javax.ws.rs.NotFoundException; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; @@ -30,7 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger; * If the list of servers contains more than one server: the server actually used is the first one * The class further implements a delayed access to the server after a communication error occurs */ -public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient implements SubscriptionAsyncBulletinBoardClient { +public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient implements SubscriptionBulletinBoardClient { private final int MAX_RETRIES = 11; diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java index cf8d47d..ca8ef84 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java @@ -8,7 +8,6 @@ import meerkat.util.BulletinBoardUtils; import static meerkat.protobuf.BulletinBoardAPI.FilterType.*; -import java.sql.Time; import java.util.*; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; @@ -19,11 +18,11 @@ import java.util.concurrent.atomic.AtomicBoolean; */ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber { - protected final Collection clients; + protected final Collection clients; protected final BulletinBoardClient localClient; - protected Iterator clientIterator; - protected SubscriptionAsyncBulletinBoardClient currentClient; + protected Iterator clientIterator; + protected SubscriptionBulletinBoardClient currentClient; private long lastServerSwitchTime; @@ -32,7 +31,7 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber private static final Float[] BREAKPOINTS = {0.5f, 0.75f, 0.9f, 0.95f, 0.99f, 0.999f}; - public ThreadedBulletinBoardSubscriber(Collection clients, BulletinBoardClient localClient) { + public ThreadedBulletinBoardSubscriber(Collection clients, BulletinBoardClient localClient) { this.clients = clients; this.localClient = localClient; diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java index a91f8d6..4a5fe62 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java @@ -17,7 +17,6 @@ import java.util.*; import java.util.concurrent.Semaphore; import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.startsWith; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; @@ -38,7 +37,7 @@ public class GenericSubscriptionClientTester { private static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt"; private static String CERT3_PEM_EXAMPLE = "/certs/enduser-certs/user3.crt"; - private SubscriptionAsyncBulletinBoardClient bulletinBoardClient; + private SubscriptionBulletinBoardClient bulletinBoardClient; private Random random; private BulletinBoardMessageGenerator generator; @@ -46,7 +45,7 @@ public class GenericSubscriptionClientTester { private Semaphore jobSemaphore; private Vector thrown; - public GenericSubscriptionClientTester(SubscriptionAsyncBulletinBoardClient bulletinBoardClient){ + public GenericSubscriptionClientTester(SubscriptionBulletinBoardClient bulletinBoardClient){ this.bulletinBoardClient = bulletinBoardClient; diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java index d2039ae..0ab5c67 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java @@ -48,7 +48,7 @@ public class LocalBulletinBoardClientTest { throw new CommunicationException(e.getCause() + " " + e.getMessage()); } - BulletinBoardServer server = new BulletinBoardSQLServer(queryProvider); + DeletableBulletinBoardServer server = new BulletinBoardSQLServer(queryProvider); server.init(DB_NAME); LocalBulletinBoardClient client = new LocalBulletinBoardClient(server, THREAD_NUM, SUBSRCIPTION_DELAY); diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java index ab82ab1..cebeb8e 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java @@ -6,6 +6,7 @@ import java.util.*; import com.google.protobuf.*; import com.google.protobuf.Timestamp; +import com.sun.org.apache.xpath.internal.operations.Bool; import meerkat.bulletinboard.*; import meerkat.bulletinboard.sqlserver.mappers.*; import static meerkat.bulletinboard.BulletinBoardConstants.*; @@ -16,6 +17,7 @@ import meerkat.comm.MessageOutputStream; import meerkat.crypto.concrete.ECDSASignature; import meerkat.crypto.concrete.SHA256Digest; +import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.protobuf.Crypto.Signature; import meerkat.protobuf.Crypto.SignatureVerificationKey; @@ -38,7 +40,7 @@ import org.springframework.jdbc.support.KeyHolder; /** * This is a generic SQL implementation of the BulletinBoardServer API. */ -public class BulletinBoardSQLServer implements BulletinBoardServer{ +public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ /** * This interface provides the required implementation-specific data to enable an access to an actual SQL server. @@ -67,6 +69,16 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{ new int[] {Types.BLOB, Types.TIMESTAMP, Types.BLOB} ), + DELETE_MSG_BY_ENTRY( + new String[] {"EntryNum"}, + new int[] {Types.INTEGER} + ), + + DELETE_MSG_BY_ID( + new String[] {"MsgId"}, + new int[] {Types.BLOB} + ), + INSERT_NEW_TAG( new String[] {"Tag"}, new int[] {Types.VARCHAR} @@ -529,6 +541,38 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{ return postMessage(msg, true); // Perform a post and check the signature for authenticity } + @Override + public BoolMsg deleteMessage(MessageID msgID) throws CommunicationException { + + String sql = sqlQueryProvider.getSQLString(QueryType.DELETE_MSG_BY_ID); + Map namedParameters = new HashMap(); + + namedParameters.put(QueryType.DELETE_MSG_BY_ID.getParamName(0),msgID); + + int affectedRows = jdbcTemplate.update(sql, namedParameters); + + //TODO: Log + + return BoolMsg.newBuilder().setValue(affectedRows > 0).build(); + + } + + @Override + public BoolMsg deleteMessage(long entryNum) throws CommunicationException { + + String sql = sqlQueryProvider.getSQLString(QueryType.DELETE_MSG_BY_ENTRY); + Map namedParameters = new HashMap(); + + namedParameters.put(QueryType.DELETE_MSG_BY_ENTRY.getParamName(0),entryNum); + + int affectedRows = jdbcTemplate.update(sql, namedParameters); + + //TODO: Log + + return BoolMsg.newBuilder().setValue(affectedRows > 0).build(); + + } + /** * This is a container class for and SQL string builder and a MapSqlParameterSource to be used with it diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java index 44a55da..bef6d68 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java @@ -30,19 +30,28 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider switch(queryType) { case ADD_SIGNATURE: - return "INSERT INTO SignatureTable (EntryNum, SignerId, Signature)" - + " SELECT DISTINCT :EntryNum AS Entry, :SignerId AS Id, :Signature AS Sig FROM UtilityTable AS Temp" - + " WHERE NOT EXISTS" - + " (SELECT 1 FROM SignatureTable AS SubTable WHERE SubTable.SignerId = :SignerId AND SubTable.EntryNum = :EntryNum)"; + return MessageFormat.format( + "INSERT INTO SignatureTable (EntryNum, SignerId, Signature)" + + " SELECT DISTINCT :{0} AS Entry, :{1} AS Id, :{2} AS Sig FROM UtilityTable AS Temp" + + " WHERE NOT EXISTS" + + " (SELECT 1 FROM SignatureTable AS SubTable WHERE SubTable.EntryNum = :{0} AND SubTable.SignerId = :{1})", + QueryType.ADD_SIGNATURE.getParamName(0), + QueryType.ADD_SIGNATURE.getParamName(1), + QueryType.ADD_SIGNATURE.getParamName(2)); case CONNECT_TAG: - return "INSERT INTO MsgTagTable (TagId, EntryNum)" - + " SELECT DISTINCT TagTable.TagId, :EntryNum AS NewEntry FROM TagTable WHERE Tag = :Tag" - + " AND NOT EXISTS (SELECT 1 FROM MsgTagTable AS SubTable WHERE SubTable.TagId = TagTable.TagId" - + " AND SubTable.EntryNum = :EntryNum)"; + return MessageFormat.format( + "INSERT INTO MsgTagTable (TagId, EntryNum)" + + " SELECT DISTINCT TagTable.TagId, :{0} AS NewEntry FROM TagTable WHERE Tag = :{1}" + + " AND NOT EXISTS (SELECT 1 FROM MsgTagTable AS SubTable WHERE SubTable.TagId = TagTable.TagId" + + " AND SubTable.EntryNum = :{0})", + QueryType.CONNECT_TAG.getParamName(0), + QueryType.CONNECT_TAG.getParamName(1)); case FIND_MSG_ID: - return "SELECT EntryNum From MsgTable WHERE MsgId = :MsgId"; + return MessageFormat.format( + "SELECT EntryNum From MsgTable WHERE MsgId = :{0}", + QueryType.FIND_MSG_ID.getParamName(0)); case FIND_TAG_ID: return MessageFormat.format( @@ -59,14 +68,32 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable"; case GET_SIGNATURES: - return "SELECT Signature FROM SignatureTable WHERE EntryNum = :EntryNum"; + return MessageFormat.format( + "SELECT Signature FROM SignatureTable WHERE EntryNum = :{0}", + QueryType.GET_SIGNATURES.getParamName(0)); case INSERT_MSG: - return "INSERT INTO MsgTable (MsgId, Msg, ExactTime) VALUES(:MsgId,:Msg,:TimeStamp)"; + return MessageFormat.format( + "INSERT INTO MsgTable (MsgId, ExactTime, Msg) VALUES(:{0}, :{1}, :{2})", + QueryType.INSERT_MSG.getParamName(0), + QueryType.INSERT_MSG.getParamName(1), + QueryType.INSERT_MSG.getParamName(2)); + + case DELETE_MSG_BY_ENTRY: + return MessageFormat.format( + "DELETE FROM MsgTable WHERE EntryNum = :{0}", + QueryType.DELETE_MSG_BY_ENTRY.getParamName(0)); + + case DELETE_MSG_BY_ID: + return MessageFormat.format( + "DELETE FROM MsgTable WHERE MsgId = :{0}", + QueryType.DELETE_MSG_BY_ID.getParamName(0)); case INSERT_NEW_TAG: - return "INSERT INTO TagTable(Tag) SELECT DISTINCT :Tag AS NewTag FROM UtilityTable WHERE" - + " NOT EXISTS (SELECT 1 FROM TagTable AS SubTable WHERE SubTable.Tag = :Tag)"; + return MessageFormat.format( + "INSERT INTO TagTable(Tag) SELECT DISTINCT :Tag AS NewTag FROM UtilityTable WHERE" + + " NOT EXISTS (SELECT 1 FROM TagTable AS SubTable WHERE SubTable.Tag = :{0})", + QueryType.INSERT_NEW_TAG.getParamName(0)); case GET_LAST_MESSAGE_ENTRY: return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable"; diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java index adf96a4..ad3df68 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java @@ -1,7 +1,5 @@ package meerkat.bulletinboard.sqlserver; -import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; -import meerkat.bulletinboard.BulletinBoardConstants; import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider; import meerkat.protobuf.BulletinBoardAPI.FilterType; import org.apache.commons.dbcp2.BasicDataSource; @@ -81,6 +79,16 @@ public class MySQLQueryProvider implements SQLQueryProvider { QueryType.INSERT_MSG.getParamName(1), QueryType.INSERT_MSG.getParamName(2)); + case DELETE_MSG_BY_ENTRY: + return MessageFormat.format( + "DELETE IGNORE FROM MsgTable WHERE EntryNum = :{0}", + QueryType.DELETE_MSG_BY_ENTRY.getParamName(0)); + + case DELETE_MSG_BY_ID: + return MessageFormat.format( + "DELETE IGNORE FROM MsgTable WHERE MsgId = :{0}", + QueryType.DELETE_MSG_BY_ID.getParamName(0)); + case INSERT_NEW_TAG: return MessageFormat.format( "INSERT IGNORE INTO TagTable(Tag) VALUES (:{0})", diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java index 245eddf..9d7d5b8 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java @@ -47,12 +47,12 @@ public interface BulletinBoardClient { /** * Create a SyncQuery to test against that corresponds with the current server state for a specific filter list * Should only be called on instances for which the actual server contacted is known (i.e. there is only one server) - * @param GenerateSyncQueryParams defines the required information needed to generate the query + * @param generateSyncQueryParams defines the required information needed to generate the query * These are represented as fractions of the total number of relevant messages * @return The generated SyncQuery * @throws CommunicationException when no DB can be contacted */ - SyncQuery generateSyncQuery(GenerateSyncQueryParams GenerateSyncQueryParams) throws CommunicationException; + SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException; /** * Closes all connections, if any. diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardMessageDeleter.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardMessageDeleter.java new file mode 100644 index 0000000..6025719 --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardMessageDeleter.java @@ -0,0 +1,30 @@ +package meerkat.bulletinboard; + +import com.google.common.util.concurrent.FutureCallback; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.BulletinBoardAPI.*; + +/** + * Created by Arbel Deutsch Peled on 13-Apr-16. + * This interface is meant to extend a BulletinBoardClient interface/class + * It provides it with the ability to delete messages from the Server + * This assumes the Server implements the {@link DeletableBulletinBoardServer} + */ +public interface BulletinBoardMessageDeleter { + + /** + * Deletes a message from a Bulletin Board Server + * @param msgID is the ID of the message to delete + * @param callback handles the result of the operation + */ + public void deleteMessage(MessageID msgID, FutureCallback callback); + + /** + * Deletes a message from the Bulletin Board + * Logs this action + * @param entryNum is the serial entry number of the message to delete + * @param callback handles the result of the operation + */ + public void deleteMessage(long entryNum, FutureCallback callback); + +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/DeletableBulletinBoardServer.java b/meerkat-common/src/main/java/meerkat/bulletinboard/DeletableBulletinBoardServer.java new file mode 100644 index 0000000..03e0c3f --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/DeletableBulletinBoardServer.java @@ -0,0 +1,29 @@ +package meerkat.bulletinboard; + +import meerkat.comm.CommunicationException; +import meerkat.protobuf.BulletinBoardAPI.*; + +/** + * Created by Arbel Deutsch Peled on 13-Apr-16. + */ +public interface DeletableBulletinBoardServer extends BulletinBoardServer { + + /** + * Deletes a message from the Bulletin Board + * Logs this action + * @param msgID is the ID of the message to delete + * @return a BoolMsg containing the value TRUE if a message was deleted, FALSE if the message does not exist + * @throws CommunicationException in case of an error + */ + public BoolMsg deleteMessage(MessageID msgID) throws CommunicationException; + + /** + * Deletes a message from the Bulletin Board + * Logs this action + * @param entryNum is the serial entry number of the message to delete + * @return a BoolMsg containing the value TRUE if a message was deleted, FALSE if the message does not exist + * @throws CommunicationException in case of an error + */ + public BoolMsg deleteMessage(long entryNum) throws CommunicationException; + +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/DeletableSubscriptionBulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/DeletableSubscriptionBulletinBoardClient.java new file mode 100644 index 0000000..acc29fe --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/DeletableSubscriptionBulletinBoardClient.java @@ -0,0 +1,7 @@ +package meerkat.bulletinboard; + +/** + * Created by Arbel Deutsch Peled on 13-Apr-16. + */ +public interface DeletableSubscriptionBulletinBoardClient extends SubscriptionBulletinBoardClient, BulletinBoardMessageDeleter { +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/SubscriptionAsyncBulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/SubscriptionAsyncBulletinBoardClient.java deleted file mode 100644 index b07e655..0000000 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/SubscriptionAsyncBulletinBoardClient.java +++ /dev/null @@ -1,7 +0,0 @@ -package meerkat.bulletinboard; - -/** - * Created by Arbel Deutsch Peled on 03-Mar-16. - */ -public interface SubscriptionAsyncBulletinBoardClient extends AsyncBulletinBoardClient, BulletinBoardSubscriber { -} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/SubscriptionBulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/SubscriptionBulletinBoardClient.java new file mode 100644 index 0000000..5577bac --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/SubscriptionBulletinBoardClient.java @@ -0,0 +1,7 @@ +package meerkat.bulletinboard; + +/** + * Created by Arbel Deutsch Peled on 03-Mar-16. + */ +public interface SubscriptionBulletinBoardClient extends AsyncBulletinBoardClient, BulletinBoardSubscriber { +} From 9ed728fca7637473b5d825d35e2fbccdde55e69c Mon Sep 17 00:00:00 2001 From: "arbel.peled" Date: Thu, 14 Apr 2016 09:20:11 +0300 Subject: [PATCH 02/15] Added message counting ability to the server (but not to the client) Added synchronous CompleteBatch read by the client Started implementing the synchronizer Added support for null callbacks --- .../CachedBulletinBoardClient.java | 48 +++-- .../LocalBulletinBoardClient.java | 82 +++++++- .../bulletinboard/MultiServerWorker.java | 6 +- .../SimpleBulletinBoardClient.java | 65 ++++-- .../SimpleBulletinBoardSynchronizer.java | 188 ++++++++++++++++++ .../SingleServerBulletinBoardClient.java | 42 ++-- .../ThreadedBulletinBoardSubscriber.java | 13 +- .../sqlserver/BulletinBoardSQLServer.java | 42 ++-- .../webapp/BulletinBoardWebApp.java | 12 +- .../bulletinboard/BulletinBoardClient.java | 10 +- .../bulletinboard/BulletinBoardConstants.java | 1 + .../bulletinboard/BulletinBoardServer.java | 10 +- .../BulletinBoardSynchronizer.java | 74 ++++++- .../meerkat/bulletinboard/CompleteBatch.java | 33 ++- 14 files changed, 532 insertions(+), 94 deletions(-) create mode 100644 bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java index 6aac297..c8b26c7 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java @@ -84,7 +84,8 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien @Override public void onFailure(Throwable t) { - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } }); @@ -101,7 +102,8 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien @Override public void onFailure(Throwable t) { - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } }); @@ -118,7 +120,8 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien @Override public void onFailure(Throwable t) { - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } }); @@ -136,7 +139,8 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien @Override public void onFailure(Throwable t) { - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } }); @@ -153,7 +157,8 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien @Override public void onFailure(Throwable t) { - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } }); @@ -171,7 +176,8 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien @Override public void onFailure(Throwable t) { - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } }); @@ -188,7 +194,8 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien @Override public void onFailure(Throwable t) { - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } }); @@ -205,7 +212,8 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien @Override public void onFailure(Throwable t) { - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } }); @@ -233,7 +241,8 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien localClient.readBatch(batchSpecificationMessage, new FutureCallback() { @Override public void onSuccess(CompleteBatch result) { - callback.onSuccess(result); // Read from local client was successful + if (callback != null) + callback.onSuccess(result); // Read from local client was successful } @Override @@ -255,7 +264,8 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien public void onFailure(Throwable t) {} }); - callback.onSuccess(result); + if (callback != null) + callback.onSuccess(result); } @@ -263,7 +273,8 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien public void onFailure(Throwable t) { // Read from remote was unsuccessful: report error - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } @@ -301,11 +312,16 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien } @Override - public List readMessages(MessageFilterList filterList) { + public List readMessages(MessageFilterList filterList) throws CommunicationException { subscriber.subscribe(filterList, new SubscriptionStoreCallback()); return localClient.readMessages(filterList); } + @Override + public CompleteBatch readBatch(BatchSpecificationMessage batchSpecificationMessage) throws CommunicationException { + return localClient.readBatch(batchSpecificationMessage); + } + @Override public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { return localClient.generateSyncQuery(generateSyncQueryParams); @@ -327,12 +343,4 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien subscriber.subscribe(filterList, startEntry, callback); } - public int syncStatus(){ - return 0; - } - - public void reSync(){ - - } - } \ No newline at end of file diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java index 1f56690..30ae462 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java @@ -291,6 +291,31 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo } + private class BatchDataReader implements Callable> { + + private final BatchSpecificationMessage batchSpecificationMessage; + + public BatchDataReader(BatchSpecificationMessage batchSpecificationMessage) { + this.batchSpecificationMessage = batchSpecificationMessage; + } + + @Override + public List call() throws Exception { + + ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); + MessageOutputStream outputStream = new MessageOutputStream<>(byteOutputStream); + server.readBatch(batchSpecificationMessage, outputStream); + + MessageInputStream inputStream = + MessageInputStreamFactory.createMessageInputStream( + new ByteArrayInputStream(byteOutputStream.toByteArray()), + BatchData.class); + + return inputStream.asList(); + + } + } + @Override public void readMessages(MessageFilterList filterList, FutureCallback> callback) { Futures.addCallback(executorService.submit(new MessageReader(filterList)), callback); @@ -310,7 +335,8 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo public void onSuccess(List result) { // Report new messages to user - callback.onSuccess(result); + if (callback != null) + callback.onSuccess(result); MessageFilterList.Builder filterBuilder = filterList.toBuilder(); @@ -339,7 +365,8 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo public void onFailure(Throwable t) { // Notify caller about failure and terminate subscription - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } } @@ -503,7 +530,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo } @Override - public List readMessages(MessageFilterList filterList) { + public List readMessages(MessageFilterList filterList) throws CommunicationException{ try { @@ -511,7 +538,40 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo return reader.call(); } catch (Exception e){ - return null; + throw new CommunicationException("Error reading from server"); + } + + } + + @Override + public CompleteBatch readBatch(BatchSpecificationMessage batchSpecificationMessage) throws CommunicationException { + + MessageFilterList filterList = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.TAG) + .setTag(BulletinBoardConstants.BATCH_TAG) + .build()) + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.TAG) + .setTag(BulletinBoardConstants.BATCH_ID_TAG_PREFIX + batchSpecificationMessage.getBatchId()) + .build()) + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.SIGNER_ID) + .setId(batchSpecificationMessage.getSignerId()) + .build()) + .build(); + + BulletinBoardMessage batchMessage = readMessages(filterList).get(0); + + BatchDataReader batchDataReader = new BatchDataReader(batchSpecificationMessage); + + try { + + List batchDataList = batchDataReader.call(); + return new CompleteBatch(batchMessage, batchDataList); + + } catch (Exception e) { + throw new CommunicationException("Error reading batch"); } } @@ -525,9 +585,12 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo public void deleteMessage(MessageID msgID, FutureCallback callback) { try { - callback.onSuccess(server.deleteMessage(msgID).getValue()); + Boolean deleted = server.deleteMessage(msgID).getValue(); + if (callback != null) + callback.onSuccess(deleted); } catch (CommunicationException e) { - callback.onFailure(e); + if (callback != null) + callback.onFailure(e); } } @@ -536,9 +599,12 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo public void deleteMessage(long entryNum, FutureCallback callback) { try { - callback.onSuccess(server.deleteMessage(entryNum).getValue()); + Boolean deleted = server.deleteMessage(entryNum).getValue(); + if (callback != null) + callback.onSuccess(deleted); } catch (CommunicationException e) { - callback.onFailure(e); + if (callback != null) + callback.onFailure(e); } } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerWorker.java index 7347f47..60327fa 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerWorker.java @@ -74,7 +74,8 @@ public abstract class MultiServerWorker extends BulletinClientWorker extends BulletinClientWorker readMessages(MessageFilterList filterList) { - WebTarget webTarget; - Response response; - BulletinBoardMessageList messageList; - // Replace null filter list with blank one. if (filterList == null){ - filterList = MessageFilterList.newBuilder().build(); + filterList = MessageFilterList.getDefaultInstance(); } for (String db : meerkatDBs) { try { - webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH); - response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF)); + SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(db, filterList, 0); - messageList = response.readEntity(BulletinBoardMessageList.class); + List result = worker.call(); - if (messageList != null){ - return messageList.getMessageList(); - } + return result; - } catch (Exception e) {} + } catch (Exception ignored) {} + } + return null; + + } + + @Override + public CompleteBatch readBatch(BatchSpecificationMessage batchSpecificationMessage) throws CommunicationException { + + // Create job with no retries for retrieval of the Bulletin Board Message that defines the batch + + MessageFilterList filterList = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.TAG) + .setTag(BulletinBoardConstants.BATCH_TAG) + .build()) + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.TAG) + .setTag(BulletinBoardConstants.BATCH_ID_TAG_PREFIX + batchSpecificationMessage.getBatchId()) + .build()) + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.SIGNER_ID) + .setId(batchSpecificationMessage.getSignerId()) + .build()) + .build(); + + for (String db : meerkatDBs) { + + try { + SingleServerReadMessagesWorker messagesWorker = new SingleServerReadMessagesWorker(db, filterList, 0); + + List messages = messagesWorker.call(); + + if (messages == null || messages.size() < 1) + continue; + + BulletinBoardMessage batchMessage = messages.get(0); + + SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(db, batchSpecificationMessage, 0); + + List batchDataList = batchWorker.call(); + + CompleteBatch result = new CompleteBatch(batchMessage, batchDataList); + + return result; + + } catch (Exception ignored) {} } return null; diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java new file mode 100644 index 0000000..1eed944 --- /dev/null +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java @@ -0,0 +1,188 @@ +package meerkat.bulletinboard; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.protobuf.ByteString; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.util.BulletinBoardUtils; + +import java.util.LinkedList; +import java.util.List; + +/** + * Created by Arbel on 13/04/2016. + * Simple, straightforward implementation of the {@link BulletinBoardSynchronizer} interface + */ +public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronizer { + + private DeletableSubscriptionBulletinBoardClient localClient; + private AsyncBulletinBoardClient remoteClient; + + private volatile SyncStatus syncStatus; + + private List> messageCountCallbacks; + private List> syncStatusCallbacks; + + private static final MessageFilterList EMPTY_FILTER = MessageFilterList.getDefaultInstance(); + private static final int SLEEP_INTERVAL = 10000; // 10 Seconds + + private class SyncCallback implements FutureCallback> { + + @Override + public void onSuccess(List result) { + + SyncStatus newStatus = SyncStatus.PENDING; + + if (result.size() == 0) { + newStatus = SyncStatus.SYNCHRONIZED; + } + + else{ // Upload messages + + for (BulletinBoardMessage message : result){ + + if (message.getMsg().getTagList().contains(BulletinBoardConstants.BATCH_TAG)){ + + // This is a batch message: need to upload batch data as well as the message itself + ByteString signerId = message.getSig(0).getSignerId(); + long batchID = Long.parseLong(BulletinBoardUtils.findTagWithPrefix(message, BulletinBoardConstants.BATCH_ID_TAG_PREFIX)); + + BatchSpecificationMessage batchSpecificationMessage = BatchSpecificationMessage.newBuilder().build(); + + localClient.readBatch(batchSpecificationMessage, null); + + } + else{ + + // This is a regular message: post it + try { + remoteClient.postMessage(message); + } catch (CommunicationException e) { + newStatus = SyncStatus.SERVER_ERROR; + } + + } + } + + } + + updateSyncStatus(newStatus); + + } + + @Override + public void onFailure(Throwable t) { + + updateSyncStatus(SyncStatus.SERVER_ERROR); + + } + + } + + public SimpleBulletinBoardSynchronizer() { + this.syncStatus = SyncStatus.STOPPED; + } + + private synchronized void updateSyncStatus(SyncStatus newStatus) { + + if (newStatus != syncStatus){ + + syncStatus = newStatus; + + for (FutureCallback callback : syncStatusCallbacks){ + if (callback != null) + callback.onSuccess(syncStatus); + } + + } + + } + + @Override + public void init(DeletableSubscriptionBulletinBoardClient localClient, AsyncBulletinBoardClient remoteClient) { + + updateSyncStatus(SyncStatus.STOPPED); + + this.localClient = localClient; + this.remoteClient = remoteClient; + + messageCountCallbacks = new LinkedList<>(); + syncStatusCallbacks = new LinkedList<>(); + + } + + @Override + public SyncStatus getSyncStatus() { + return syncStatus; + } + + @Override + public void subscribeToSyncStatus(FutureCallback callback) { + syncStatusCallbacks.add(callback); + } + + @Override + public List getRemainingMessages() throws CommunicationException{ + return localClient.readMessages(EMPTY_FILTER); + } + + @Override + public void getRemainingMessages(FutureCallback> callback) { + localClient.readMessages(EMPTY_FILTER, callback); + } + + @Override + public long getRemainingMessagesCount() throws CommunicationException { + return localClient.readMessages(EMPTY_FILTER).size(); + } + + @Override + public void subscribeToRemainingMessagesCount(FutureCallback callback) { + messageCountCallbacks.add(callback); + } + + @Override + public void run() { + + if (syncStatus != SyncStatus.STOPPED) { + + updateSyncStatus(SyncStatus.PENDING); + SyncCallback callback = new SyncCallback(); + + while (syncStatus != SyncStatus.STOPPED) { + + try { + + do { + localClient.readMessages(EMPTY_FILTER, callback); + } while (syncStatus == SyncStatus.PENDING); + + synchronized (this) { + this.wait(SLEEP_INTERVAL); + } + + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } + + } + + } + + @Override + public void nudge() { + synchronized (this) { + this.notify(); + } + } + + @Override + public void stop() { + + updateSyncStatus(SyncStatus.STOPPED); + + } + +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java index f12430d..0a43562 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java @@ -97,7 +97,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i @Override public void onSuccess(T result) { - futureCallback.onSuccess(result); + if (futureCallback != null) + futureCallback.onSuccess(result); } @Override @@ -115,7 +116,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i scheduleWorker(worker, this); } else { // No more retries: notify caller about failure - futureCallback.onFailure(t); + if (futureCallback != null) + futureCallback.onFailure(t); } } @@ -150,7 +152,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i } if (batchDataRemaining.decrementAndGet() == 0){ - callback.onSuccess(this.aggregatedResult.get()); + if (callback != null) + callback.onSuccess(this.aggregatedResult.get()); } } @@ -158,7 +161,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i public void onFailure(Throwable t) { // Notify caller about failure - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } } @@ -195,26 +199,16 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i if (remainingQueries.decrementAndGet() == 0){ - String batchIdStr = BulletinBoardUtils.findTagWithPrefix(batchMessage, BulletinBoardConstants.BATCH_ID_TAG_PREFIX); - - if (batchIdStr == null){ - callback.onFailure(new CommunicationException("Server returned invalid message with no Batch ID tag")); - } - - BeginBatchMessage beginBatchMessage = - BeginBatchMessage.newBuilder() - .setSignerId(batchMessage.getSig(0).getSignerId()) - .setBatchId(Integer.parseInt(batchIdStr)) - .addAllTag(BulletinBoardUtils.removePrefixTags(batchMessage, Arrays.asList(prefixes))) - .build(); - callback.onSuccess(new CompleteBatch(beginBatchMessage, batchDataList, batchMessage.getSig(0))); + if (callback != null) + callback.onSuccess(new CompleteBatch(batchMessage, batchDataList)); } } protected void fail(Throwable t) { if (failed.compareAndSet(false, true)) { - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } } @@ -289,7 +283,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i public void onSuccess(List result) { // Report new messages to user - callback.onSuccess(result); + if (callback != null) + callback.onSuccess(result); // Remove last filter from list (MIN_ENTRY one) filterBuilder.removeFilter(filterBuilder.getFilterCount() - 1); @@ -315,7 +310,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i fail(); // Notify caller about failure and terminate subscription - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } } @@ -397,7 +393,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i @Override public void onFailure(Throwable t) { - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } } @@ -425,7 +422,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i @Override public void onFailure(Throwable t) { - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java index ca8ef84..9568255 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java @@ -29,6 +29,8 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber private AtomicBoolean isSyncInProgress; private Semaphore rescheduleSemaphore; + private AtomicBoolean stopped; + private static final Float[] BREAKPOINTS = {0.5f, 0.75f, 0.9f, 0.95f, 0.99f, 0.999f}; public ThreadedBulletinBoardSubscriber(Collection clients, BulletinBoardClient localClient) { @@ -44,6 +46,8 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber isSyncInProgress = new AtomicBoolean(false); rescheduleSemaphore = new Semaphore(1); + stopped = new AtomicBoolean(false); + } /** @@ -131,7 +135,8 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber //TODO: log - callback.onFailure(e); // Hard error: Cannot guarantee subscription safety + if (callback != null) + callback.onFailure(e); // Hard error: Cannot guarantee subscription safety } @@ -217,7 +222,8 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber public void onSuccess(List result) { // Propagate result to caller - callback.onSuccess(result); + if (callback != null) + callback.onSuccess(result); // Renew subscription @@ -249,7 +255,8 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber public void onSuccess(List result) { // Propagate result to caller - callback.onSuccess(result); + if (callback != null) + callback.onSuccess(result); } diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java index cebeb8e..6b2ef4f 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java @@ -679,6 +679,30 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ } + @Override + public IntMsg getMessageCount(MessageFilterList filterList) throws CommunicationException { + + BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder(); + + // SQL length is roughly 50 characters per filter + 50 for the query itself + StringBuilder sqlBuilder = new StringBuilder(50 * (filterList.getFilterCount() + 1)); + + // Check if Tag/Signature tables are required for filtering purposes + + sqlBuilder.append(sqlQueryProvider.getSQLString(QueryType.COUNT_MESSAGES)); + + // Get conditions + + SQLAndParameters sqlAndParameters = getSQLFromFilters(filterList); + sqlBuilder.append(sqlAndParameters.sql); + + // Run query and stream the output using a MessageCallbackHandler + + List count = jdbcTemplate.query(sqlBuilder.toString(), sqlAndParameters.parameters, new LongMapper()); + return IntMsg.newBuilder().setValue(count.get(0).intValue()).build(); + + } + /** * This method returns a string representation of the tag associated with a batch ID @@ -713,23 +737,9 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ .build()) .build(); - // SQL length is roughly 50 characters per filter + 50 for the query itself - StringBuilder sqlBuilder = new StringBuilder(50 * (filterList.getFilterCount() + 1)); + int count = getMessageCount(filterList).getValue(); - // Check if Tag/Signature tables are required for filtering purposes - - sqlBuilder.append(sqlQueryProvider.getSQLString(QueryType.COUNT_MESSAGES)); - - // Get conditions - - SQLAndParameters sqlAndParameters = getSQLFromFilters(filterList); - sqlBuilder.append(sqlAndParameters.sql); - - // Run query and stream the output using a MessageCallbackHandler - - List count = jdbcTemplate.query(sqlBuilder.toString(), sqlAndParameters.parameters, new LongMapper()); - - return (count.size() > 0) && (count.get(0) > 0); + return count > 0; } diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java index 7c0f7fa..74b5f2a 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java @@ -15,14 +15,12 @@ import meerkat.bulletinboard.sqlserver.MySQLQueryProvider; import meerkat.bulletinboard.sqlserver.SQLiteQueryProvider; import meerkat.comm.CommunicationException; import meerkat.comm.MessageOutputStream; -import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.BulletinBoardAPI.*; import static meerkat.bulletinboard.BulletinBoardConstants.*; import static meerkat.rest.Constants.*; import java.io.IOException; import java.io.OutputStream; -import java.util.Collection; /** * An implementation of the BulletinBoardServer which functions as a WebApp @@ -99,6 +97,16 @@ public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextL bulletinBoard.readMessages(filterList, out); } + @Path(COUNT_MESSAGES_PATH) + @POST + @Consumes(MEDIATYPE_PROTOBUF) + @Produces(MEDIATYPE_PROTOBUF) + @Override + public IntMsg getMessageCount(MessageFilterList filterList) throws CommunicationException { + init(); + return bulletinBoard.getMessageCount(filterList); + } + @Path(READ_MESSAGES_PATH) @POST diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java index 9d7d5b8..91e577d 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java @@ -42,7 +42,15 @@ public interface BulletinBoardClient { * @param filterList return only messages that match the filters (null means no filtering) * @return the list of messages */ - List readMessages(MessageFilterList filterList); + List readMessages(MessageFilterList filterList) throws CommunicationException; + + /** + * Read a given batch message from the bulletin board + * @param batchSpecificationMessage contains the data required to specify a single batch instance + * @return the complete batch + * @throws CommunicationException + */ + CompleteBatch readBatch(BatchSpecificationMessage batchSpecificationMessage) throws CommunicationException; /** * Create a SyncQuery to test against that corresponds with the current server state for a specific filter list diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardConstants.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardConstants.java index 9ddee90..f2fb3a9 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardConstants.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardConstants.java @@ -10,6 +10,7 @@ public interface BulletinBoardConstants { public static final String BULLETIN_BOARD_SERVER_PATH = "/bbserver"; public static final String GENERATE_SYNC_QUERY_PATH = "/generatesyncquery"; public static final String READ_MESSAGES_PATH = "/readmessages"; + public static final String COUNT_MESSAGES_PATH = "/countmessages"; public static final String READ_BATCH_PATH = "/readbatch"; public static final String POST_MESSAGE_PATH = "/postmessage"; public static final String BEGIN_BATCH_PATH = "/beginbatch"; diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java index e458dbc..e26c54a 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java @@ -32,13 +32,21 @@ public interface BulletinBoardServer{ public BoolMsg postMessage(BulletinBoardMessage msg) throws CommunicationException; /** - * Read all messages posted matching the given filter + * Read all posted messages matching the given filters * @param filterList return only messages that match the filters (empty list or null means no filtering) * @param out is an output stream into which the matching messages are written * @throws CommunicationException on DB connection error */ public void readMessages(MessageFilterList filterList, MessageOutputStream out) throws CommunicationException; + /** + * Return the number of posted messages matching the given filters + * @param filterList count only messages that match the filters (empty list or null means no filtering) + * @return an IntMsg containing the number of messages that match the filter + * @throws CommunicationException on DB connection error + */ + public IntMsg getMessageCount(MessageFilterList filterList) throws CommunicationException; + /** * Informs server about a new batch message * @param message contains the required data about the new batch diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSynchronizer.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSynchronizer.java index 4b07225..569d4ef 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSynchronizer.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSynchronizer.java @@ -1,22 +1,84 @@ package meerkat.bulletinboard; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.BulletinBoardAPI.*; + +import com.google.common.util.concurrent.FutureCallback; + +import java.util.List; + /** * Created by Arbel Deutsch Peled on 08-Mar-16. * This interface defines the behaviour of a bulletin board synchronizer * This is used to make sure that data in a specific instance of a bulletin board server is duplicated to a sufficient percentage of the other servers */ -public interface BulletinBoardSynchronizer extends Runnable{ +public interface BulletinBoardSynchronizer extends Runnable { + + public enum SyncStatus{ + SYNCHRONIZED, // No more messages to upload + PENDING, // Synchronizer is uploading data + SERVER_ERROR, // Synchronizer encountered an error while uploading + STOPPED // Stopped/Not started by user + } /** - * - * @param localClient is a client for the local DB instance - * @param remoteClient is a client for the remote DBs - * @param minRedundancy + * Initializes the synchronizer with the required data to function properly + * @param localClient is a client for the temporary local storage server which contains only data to be uploaded + * @param remoteClient is a client for the remote servers into which the data needs to be uploaded */ - public void init(BulletinBoardClient localClient, AsyncBulletinBoardClient remoteClient, float minRedundancy); + public void init(DeletableSubscriptionBulletinBoardClient localClient, AsyncBulletinBoardClient remoteClient); + /** + * Returns the current server synchronization status + * @return the current synchronization status + */ + public SyncStatus getSyncStatus(); + + /** + * Creates a subscription to sync status changes + * @param callback is the handler for any status changes + */ + public void subscribeToSyncStatus(FutureCallback callback); + + /** + * Returns the messages which have not yet been synchronized + * @return the list of messages remaining to be synchronized + */ + public List getRemainingMessages() throws CommunicationException; + + /** + * Asynchronously returns the messages which have not yet been synchronized + * @param callback is the handler for the list of messages + */ + public void getRemainingMessages(FutureCallback> callback); + + /** + * Returns the current number of unsynchronized messages + * @return the current synchronization status + */ + public long getRemainingMessagesCount() throws CommunicationException; + + /** + * Creates a subscription to changes in the number of unsynchronized messages + * @param callback is the handler for any status changes + */ + public void subscribeToRemainingMessagesCount(FutureCallback callback); + + /** + * Starts the synchronization + */ @Override public void run(); + /** + * Lets the Synchronizer know that there is new data to be uploaded + * This is used to reduce the latency between local data-writes and uploads to the remote servers + */ + public void nudge(); + + /** + * Stops the synchronization + */ + public void stop(); } diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/CompleteBatch.java b/meerkat-common/src/main/java/meerkat/bulletinboard/CompleteBatch.java index 649fd8b..89ae80a 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/CompleteBatch.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/CompleteBatch.java @@ -3,8 +3,9 @@ package meerkat.bulletinboard; import com.google.protobuf.Timestamp; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.protobuf.Crypto.*; -import meerkat.util.BulletinBoardMessageComparator; +import meerkat.util.BulletinBoardUtils; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -49,6 +50,36 @@ public class CompleteBatch { this.timestamp = timestamp; } + /** + * Combines the actual Bulletin board representation of a batch into a CompleteBatch object + * @param batchMessage is the BulletinBoard message that specifies the batch + * @param batchDataList is the (ordered) list of batch data + */ + public CompleteBatch(BulletinBoardMessage batchMessage, List batchDataList) throws IllegalArgumentException{ + + final String[] PREFIXES = { + BulletinBoardConstants.BATCH_ID_TAG_PREFIX, + BulletinBoardConstants.BATCH_TAG}; + + String batchIdStr = BulletinBoardUtils.findTagWithPrefix(batchMessage, BulletinBoardConstants.BATCH_ID_TAG_PREFIX); + + if (batchIdStr == null){ + throw new IllegalArgumentException(""); + } + + this.beginBatchMessage = + BeginBatchMessage.newBuilder() + .setSignerId(batchMessage.getSig(0).getSignerId()) + .setBatchId(Integer.parseInt(batchIdStr)) + .addAllTag(BulletinBoardUtils.removePrefixTags(batchMessage, Arrays.asList(PREFIXES))) + .build(); + + this.batchDataList = batchDataList; + this.signature = batchMessage.getSig(0); + this.timestamp = batchMessage.getMsg().getTimestamp(); + + } + public BeginBatchMessage getBeginBatchMessage() { return beginBatchMessage; } From 4c33e923b20c4ff69fef136be1172fafbd0cf5c7 Mon Sep 17 00:00:00 2001 From: "arbel.peled" Date: Sat, 16 Apr 2016 19:50:09 +0300 Subject: [PATCH 03/15] Implemented Synchronizer and Cached Client Not tested yet --- .../CachedBulletinBoardClient.java | 23 +++++-- .../LocalBulletinBoardClient.java | 47 +++++++++------ .../SimpleBulletinBoardClient.java | 52 ++++++++++++++-- .../SimpleBulletinBoardSynchronizer.java | 60 +++++++++++++++---- .../SingleServerBulletinBoardClient.java | 15 ++--- .../bulletinboard/BulletinBoardClient.java | 8 +++ .../BulletinBoardMessageDeleter.java | 23 ++++++- 7 files changed, 177 insertions(+), 51 deletions(-) diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java index c8b26c7..ac61114 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java @@ -14,7 +14,8 @@ import java.util.List; * It provides asynchronous access to several remote servers, as well as a local cache * Read operations are performed on the local server * Batch reads are performed on the local server and, if they fail, also on the remote servers - * Write operations are performed first on the local server and then on the remotes + * Write operations are performed on the local server + * A Synchronizer is employed in order to keep the remote server up to date * After any read is carried out, a subscription is made for the specific query to make sure the local DB will be updated * The database also employs a synchronizer which makes sure local data is sent to the remote servers */ @@ -23,6 +24,9 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien private final AsyncBulletinBoardClient localClient; private AsyncBulletinBoardClient remoteClient; private BulletinBoardSubscriber subscriber; + private BulletinBoardSynchronizer synchronizer; + + private Thread syncThread; private class SubscriptionStoreCallback implements FutureCallback> { @@ -62,15 +66,21 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien * @param localClient is a Client for the local instance * @param remoteClient is a Client for the remote instance(s); Should have endless retries for post operations * @param subscriber is a subscription service to the remote instance(s) + * @param queue is a client for a local deletable server to be used as a queue for not-yet-uploaded messages */ public CachedBulletinBoardClient(AsyncBulletinBoardClient localClient, AsyncBulletinBoardClient remoteClient, - BulletinBoardSubscriber subscriber) { + BulletinBoardSubscriber subscriber, + DeletableSubscriptionBulletinBoardClient queue) { this.localClient = localClient; this.remoteClient = remoteClient; this.subscriber = subscriber; + this.synchronizer = new SimpleBulletinBoardSynchronizer(); + synchronizer.init(queue, remoteClient); + syncThread = new Thread(synchronizer); + syncThread.start(); } @Override @@ -302,8 +312,12 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien @Override public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { - localClient.postMessage(msg); - return remoteClient.postMessage(msg); + return localClient.postMessage(msg); + } + + @Override + public MessageID postBatch(CompleteBatch completeBatch) throws CommunicationException { + return localClient.postBatch(completeBatch); } @Override @@ -331,6 +345,7 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien public void close() { localClient.close(); remoteClient.close(); + synchronizer.stop(); } @Override diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java index 30ae462..109d5ed 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java @@ -57,7 +57,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo @Override - public Boolean call() throws Exception { + public Boolean call() throws CommunicationException { return server.postMessage(msg).getValue(); } @@ -83,25 +83,21 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo @Override - public Boolean call() throws Exception { + public Boolean call() throws CommunicationException { - if (!server.beginBatch(completeBatch.getBeginBatchMessage()).getValue()) - return false; + server.beginBatch(completeBatch.getBeginBatchMessage()); + + BatchMessage.Builder builder = BatchMessage.newBuilder() + .setSignerId(completeBatch.getSignature().getSignerId()) + .setBatchId(completeBatch.getBeginBatchMessage().getBatchId()); int i=0; for (BatchData data : completeBatch.getBatchDataList()){ - BatchMessage message = BatchMessage.newBuilder() - .setSignerId(completeBatch.getSignature().getSignerId()) - .setBatchId(completeBatch.getBeginBatchMessage().getBatchId()) - .setSerialNum(i) - .setData(data) - .build(); - - if (!server.postBatchMessage(message).getValue()) - return false; + server.postBatchMessage(builder.setSerialNum(i).setData(data).build()); i++; + } return server.closeBatchMessage(completeBatch.getCloseBatchMessage()).getValue(); @@ -501,17 +497,22 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo @Override public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { - try { - MessagePoster poster = new MessagePoster(msg); poster.call(); digest.update(msg); return digest.digestAsMessageID(); - } catch (Exception e) { - return null; - } + } + + @Override + public MessageID postBatch(CompleteBatch completeBatch) throws CommunicationException { + + CompleteBatchPoster poster = new CompleteBatchPoster(completeBatch); + poster.call(); + + digest.update(completeBatch); + return digest.digestAsMessageID(); } @@ -609,6 +610,16 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo } + @Override + public boolean deleteMessage(MessageID msgID) throws CommunicationException { + return server.deleteMessage(msgID).getValue(); + } + + @Override + public boolean deleteMessage(long entryNum) throws CommunicationException { + return server.deleteMessage(entryNum).getValue(); + } + @Override public void close() { try { diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java index 9f6eae7..28252ae 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java @@ -2,8 +2,7 @@ package meerkat.bulletinboard; import com.google.protobuf.ByteString; import com.google.protobuf.Timestamp; -import meerkat.bulletinboard.workers.singleserver.SingleServerReadBatchWorker; -import meerkat.bulletinboard.workers.singleserver.SingleServerReadMessagesWorker; +import meerkat.bulletinboard.workers.singleserver.*; import meerkat.comm.CommunicationException; import meerkat.comm.MessageInputStream; import meerkat.crypto.Digest; @@ -38,7 +37,7 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{ protected Client client; - protected Digest digest; + protected BatchDigest digest; /** * Stores database locations and initializes the web Client @@ -53,7 +52,8 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{ client.register(ProtobufMessageBodyReader.class); client.register(ProtobufMessageBodyWriter.class); - digest = new SHA256Digest(); + // Wrap the Digest into a BatchDigest + digest = new GenericBatchDigest(new SHA256Digest()); } @@ -92,6 +92,50 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{ return MessageID.newBuilder().setID(ByteString.copyFrom(digest.digest())).build(); } + @Override + public MessageID postBatch(CompleteBatch completeBatch) throws CommunicationException { + + int pos = 0; + ByteString signerID = completeBatch.getSignature().getSignerId(); + int batchID = completeBatch.getBeginBatchMessage().getBatchId(); + + // Post message to all databases + try { + for (String db : meerkatDBs) { + + SingleServerBeginBatchWorker beginBatchWorker = new SingleServerBeginBatchWorker(db, completeBatch.getBeginBatchMessage(), 0); + + beginBatchWorker.call(); + + BatchMessage.Builder builder = BatchMessage.newBuilder().setSignerId(signerID).setBatchId(batchID); + + for (BatchData batchData : completeBatch.getBatchDataList()) { + + SingleServerPostBatchWorker postBatchWorker = + new SingleServerPostBatchWorker( + db, + builder.setData(batchData).setSerialNum(pos).build(), + 0); + + postBatchWorker.call(); + + pos++; + + } + + SingleServerCloseBatchWorker closeBatchWorker = new SingleServerCloseBatchWorker(db, completeBatch.getCloseBatchMessage(), 0); + + closeBatchWorker.call(); + + } + } catch (Exception e) { // Occurs only when server replies with valid status but invalid data + throw new CommunicationException("Error accessing database: " + e.getMessage()); + } + + digest.update(completeBatch); + return digest.digestAsMessageID(); + } + /** * Access each database and search for a given message ID * Return the number of databases in which the message was found diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java index 1eed944..d96864a 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java @@ -26,6 +26,27 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize private static final MessageFilterList EMPTY_FILTER = MessageFilterList.getDefaultInstance(); private static final int SLEEP_INTERVAL = 10000; // 10 Seconds + private class MessageDeleteCallback implements FutureCallback { + + private final long entryNum; + + public MessageDeleteCallback(long entryNum) { + this.entryNum = entryNum; + } + + @Override + public void onSuccess(Boolean result) { + // Success: delete from database + localClient.deleteMessage(entryNum, null); + } + + @Override + public void onFailure(Throwable t) { + // Ignore + } + + } + private class SyncCallback implements FutureCallback> { @Override @@ -41,27 +62,40 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize for (BulletinBoardMessage message : result){ - if (message.getMsg().getTagList().contains(BulletinBoardConstants.BATCH_TAG)){ + try { - // This is a batch message: need to upload batch data as well as the message itself - ByteString signerId = message.getSig(0).getSignerId(); - long batchID = Long.parseLong(BulletinBoardUtils.findTagWithPrefix(message, BulletinBoardConstants.BATCH_ID_TAG_PREFIX)); + if (message.getMsg().getTagList().contains(BulletinBoardConstants.BATCH_TAG)) { - BatchSpecificationMessage batchSpecificationMessage = BatchSpecificationMessage.newBuilder().build(); + // This is a batch message: need to upload batch data as well as the message itself + ByteString signerID = message.getSig(0).getSignerId(); + int batchID = Integer.parseInt(BulletinBoardUtils.findTagWithPrefix(message, BulletinBoardConstants.BATCH_ID_TAG_PREFIX)); - localClient.readBatch(batchSpecificationMessage, null); + BatchSpecificationMessage batchSpecificationMessage = BatchSpecificationMessage.newBuilder() + .setSignerId(signerID) + .setBatchId(batchID) + .setStartPosition(0) + .build(); - } - else{ - // This is a regular message: post it - try { + CompleteBatch completeBatch = localClient.readBatch(batchSpecificationMessage); + + remoteClient.postBatch(completeBatch); + + + } else { + + // This is a regular message: post it + remoteClient.postMessage(message); - } catch (CommunicationException e) { - newStatus = SyncStatus.SERVER_ERROR; + } + localClient.deleteMessage(message.getEntryNum()); + + } catch (CommunicationException e) { + updateSyncStatus(SyncStatus.SERVER_ERROR); } + } } @@ -144,7 +178,7 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize @Override public void run() { - if (syncStatus != SyncStatus.STOPPED) { + if (syncStatus == SyncStatus.STOPPED) { updateSyncStatus(SyncStatus.PENDING); SyncCallback callback = new SyncCallback(); diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java index 0a43562..74a8162 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java @@ -34,8 +34,6 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i private ListeningScheduledExecutorService executorService; - protected BatchDigest batchDigest; - private long lastServerErrorTime; private final long failDelayInMilliseconds; @@ -347,9 +345,6 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i // Perform usual setup super.init(clientParams); - // Wrap the Digest into a BatchDigest - batchDigest = new GenericBatchDigest(digest); - // Remove all but first DB address String dbAddress = meerkatDBs.get(0); meerkatDBs = new LinkedList<>(); @@ -367,9 +362,9 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i scheduleWorker(worker, new RetryCallback<>(worker, callback)); // Calculate the correct message ID and return it - batchDigest.reset(); - batchDigest.update(msg.getMsg()); - return batchDigest.digestAsMessageID(); + digest.reset(); + digest.update(msg.getMsg()); + return digest.digestAsMessageID(); } @@ -435,9 +430,9 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i new BeginBatchCallback(completeBatch, callback) ); - batchDigest.update(completeBatch); + digest.update(completeBatch); - return batchDigest.digestAsMessageID(); + return digest.digestAsMessageID(); } diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java index 91e577d..4fb526a 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java @@ -27,6 +27,14 @@ public interface BulletinBoardClient { */ MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException; + /** + * Perform an end-to-end post of a signed batch message + * @param completeBatch contains all the data of the batch including the meta-data and the signature + * @return a unique identifier for the batch message + * @throws CommunicationException + */ + public MessageID postBatch(CompleteBatch completeBatch) throws CommunicationException; + /** * Check how "safe" a given message is in a synchronous manner * @param id is the unique message identifier for retrieval diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardMessageDeleter.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardMessageDeleter.java index 6025719..cf57975 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardMessageDeleter.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardMessageDeleter.java @@ -13,18 +13,37 @@ import meerkat.protobuf.BulletinBoardAPI.*; public interface BulletinBoardMessageDeleter { /** - * Deletes a message from a Bulletin Board Server + * Deletes a message from a Bulletin Board Server in a possibly asynchronous manner + * Logs this action * @param msgID is the ID of the message to delete * @param callback handles the result of the operation */ public void deleteMessage(MessageID msgID, FutureCallback callback); /** - * Deletes a message from the Bulletin Board + * Deletes a message from the Bulletin Board in a possibly asynchronous manner * Logs this action * @param entryNum is the serial entry number of the message to delete * @param callback handles the result of the operation */ public void deleteMessage(long entryNum, FutureCallback callback); + /** + * Deletes a message from a Bulletin Board Server in a synchronous manner + * Logs this action + * @param msgID is the ID of the message to delete + * @return TRUE if the message was deleted and FALSE if it did not exist on the server + * @throws CommunicationException when an error occurs + */ + public boolean deleteMessage(MessageID msgID) throws CommunicationException; + + /** + * Deletes a message from the Bulletin Board in a synchronous manner + * Logs this action + * @param entryNum is the serial entry number of the message to delete + * @return TRUE if the message was deleted and FALSE if it did not exist on the server + * @throws CommunicationException when an error occurs + */ + public boolean deleteMessage(long entryNum) throws CommunicationException; + } From 7c60e487cc129d791e3bdb1c9c8a69f9d7e14507 Mon Sep 17 00:00:00 2001 From: "arbel.peled" Date: Wed, 1 Jun 2016 21:34:17 +0300 Subject: [PATCH 04/15] Created a test for the Synchronizer. Not passing yet. --- .../SimpleBulletinBoardClient.java | 57 ++--- .../SimpleBulletinBoardSynchronizer.java | 110 +++++++- .../SingleServerGetRedundancyWorker.java | 2 - .../BulletinBoardSynchronizerTest.java | 241 ++++++++++++++++++ .../sqlserver/BulletinBoardSQLServer.java | 3 - .../sqlserver/H2QueryProvider.java | 8 +- .../BulletinBoardSynchronizer.java | 4 +- .../util/BulletinBoardMessageGenerator.java | 133 +++++++++- 8 files changed, 490 insertions(+), 68 deletions(-) create mode 100644 bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java index 28252ae..579ecf9 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java @@ -1,22 +1,13 @@ package meerkat.bulletinboard; import com.google.protobuf.ByteString; -import com.google.protobuf.Timestamp; import meerkat.bulletinboard.workers.singleserver.*; import meerkat.comm.CommunicationException; -import meerkat.comm.MessageInputStream; -import meerkat.crypto.Digest; import meerkat.crypto.concrete.SHA256Digest; -import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.protobuf.Voting.*; import meerkat.rest.*; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.util.Collection; -import java.util.Iterator; import java.util.List; import javax.ws.rs.client.Client; @@ -68,7 +59,7 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{ public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { WebTarget webTarget; - Response response; + Response response = null; // Post message to all databases try { @@ -80,10 +71,17 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{ if (response.getStatusInfo() == Response.Status.OK || response.getStatusInfo() == Response.Status.CREATED) { response.readEntity(BoolMsg.class).getValue(); + } else { + throw new CommunicationException("Server returned error. Status was: " + response.getStatus()); } } } catch (Exception e) { // Occurs only when server replies with valid status but invalid data - throw new CommunicationException("Error accessing database: " + e.getMessage()); + + throw new CommunicationException("Server returned invalid data type: " + e.getMessage()); + + } finally { + if (response != null) + response.close(); } // Calculate the correct message ID and return it @@ -100,36 +98,33 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{ int batchID = completeBatch.getBeginBatchMessage().getBatchId(); // Post message to all databases - try { - for (String db : meerkatDBs) { - SingleServerBeginBatchWorker beginBatchWorker = new SingleServerBeginBatchWorker(db, completeBatch.getBeginBatchMessage(), 0); + for (String db : meerkatDBs) { - beginBatchWorker.call(); + SingleServerBeginBatchWorker beginBatchWorker = new SingleServerBeginBatchWorker(db, completeBatch.getBeginBatchMessage(), 0); - BatchMessage.Builder builder = BatchMessage.newBuilder().setSignerId(signerID).setBatchId(batchID); + beginBatchWorker.call(); - for (BatchData batchData : completeBatch.getBatchDataList()) { + BatchMessage.Builder builder = BatchMessage.newBuilder().setSignerId(signerID).setBatchId(batchID); - SingleServerPostBatchWorker postBatchWorker = - new SingleServerPostBatchWorker( - db, - builder.setData(batchData).setSerialNum(pos).build(), - 0); + for (BatchData batchData : completeBatch.getBatchDataList()) { - postBatchWorker.call(); + SingleServerPostBatchWorker postBatchWorker = + new SingleServerPostBatchWorker( + db, + builder.setData(batchData).setSerialNum(pos).build(), + 0); - pos++; + postBatchWorker.call(); - } - - SingleServerCloseBatchWorker closeBatchWorker = new SingleServerCloseBatchWorker(db, completeBatch.getCloseBatchMessage(), 0); - - closeBatchWorker.call(); + pos++; } - } catch (Exception e) { // Occurs only when server replies with valid status but invalid data - throw new CommunicationException("Error accessing database: " + e.getMessage()); + + SingleServerCloseBatchWorker closeBatchWorker = new SingleServerCloseBatchWorker(db, completeBatch.getCloseBatchMessage(), 0); + + closeBatchWorker.call(); + } digest.update(completeBatch); diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java index d96864a..f7e9baa 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java @@ -8,6 +8,8 @@ import meerkat.util.BulletinBoardUtils; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; /** * Created by Arbel on 13/04/2016. @@ -25,24 +27,75 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize private static final MessageFilterList EMPTY_FILTER = MessageFilterList.getDefaultInstance(); private static final int SLEEP_INTERVAL = 10000; // 10 Seconds + private static final int WAIT_CAP = 300000; // 5 minutes wait before deciding that the sync has failed fatally + private Semaphore semaphore; + + /** + * This class is a callback that deletes a message if it has been successfully posted + * It also calls a stored callback + */ private class MessageDeleteCallback implements FutureCallback { private final long entryNum; + private final FutureCallback callback; - public MessageDeleteCallback(long entryNum) { + public MessageDeleteCallback(long entryNum, FutureCallback callback) { this.entryNum = entryNum; + this.callback = callback; } @Override public void onSuccess(Boolean result) { // Success: delete from database localClient.deleteMessage(entryNum, null); + callback.onSuccess(null); } @Override public void onFailure(Throwable t) { - // Ignore + callback.onFailure(t); + } + + } + + /** + * This class aggregates the results from all of the post operations + * If any post has failed: it changes the sync status to SERVER_ERROR + * It also notifies the main sync loop when all uploads are finished + */ + private class SyncStatusUpdateCallback implements FutureCallback { + + private int count; + private boolean errorEncountered; + + public SyncStatusUpdateCallback(int count) { + this.count = count; + this.errorEncountered = false; + } + + private void handleStatusUpdate() { + count--; + if (count <= 0) { + + if (errorEncountered) + updateSyncStatus(SyncStatus.SERVER_ERROR); + + // Upload is done: wake up the synchronizer loop + semaphore.release(); + + } + } + + @Override + public void onSuccess(Void result) { + handleStatusUpdate(); + } + + @Override + public void onFailure(Throwable t) { + errorEncountered = true; + handleStatusUpdate(); } } @@ -52,6 +105,20 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize @Override public void onSuccess(List result) { + // Notify Message Count callbacks if needed + + if (syncStatus != SyncStatus.SYNCHRONIZED || result.size() > 0) { + + for (FutureCallback callback : messageCountCallbacks){ + callback.onSuccess(result.size()); + } + + } + + // Handle upload and status change + + SyncStatusUpdateCallback syncStatusUpdateCallback = new SyncStatusUpdateCallback(result.size()); + SyncStatus newStatus = SyncStatus.PENDING; if (result.size() == 0) { @@ -79,20 +146,21 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize CompleteBatch completeBatch = localClient.readBatch(batchSpecificationMessage); - remoteClient.postBatch(completeBatch); + remoteClient.postBatch(completeBatch, new MessageDeleteCallback(message.getEntryNum(), syncStatusUpdateCallback)); } else { // This is a regular message: post it - - remoteClient.postMessage(message); + remoteClient.postMessage(message, new MessageDeleteCallback(message.getEntryNum(), syncStatusUpdateCallback)); } localClient.deleteMessage(message.getEntryNum()); } catch (CommunicationException e) { + // This is an error with the local server + // TODO: log updateSyncStatus(SyncStatus.SERVER_ERROR); } @@ -143,6 +211,8 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize messageCountCallbacks = new LinkedList<>(); syncStatusCallbacks = new LinkedList<>(); + semaphore = new Semaphore(0); + } @Override @@ -185,18 +255,32 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize while (syncStatus != SyncStatus.STOPPED) { - try { + do { + + if (syncStatus == SyncStatus.PENDING || syncStatus == SyncStatus.SERVER_ERROR) { - do { localClient.readMessages(EMPTY_FILTER, callback); - } while (syncStatus == SyncStatus.PENDING); - synchronized (this) { - this.wait(SLEEP_INTERVAL); } + try { + + semaphore.tryAcquire(WAIT_CAP, TimeUnit.MILLISECONDS); + //TODO: log hard error. Too much time trying to upload data. + + } catch (InterruptedException ignored) { + // We expect an interruption when the upload will complete + } + + + } while (syncStatus == SyncStatus.PENDING); + + // Database is synced. Wait for new data. + + try { + semaphore.tryAcquire(SLEEP_INTERVAL, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { - e.printStackTrace(); + //TODO: log (probably nudged) } } @@ -207,9 +291,7 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize @Override public void nudge() { - synchronized (this) { - this.notify(); - } + semaphore.release(); } @Override diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGetRedundancyWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGetRedundancyWorker.java index 0401a76..23c07af 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGetRedundancyWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGetRedundancyWorker.java @@ -6,7 +6,6 @@ import meerkat.comm.MessageInputStream; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.rest.Constants; -import javax.ws.rs.ProcessingException; import javax.ws.rs.client.Client; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; @@ -14,7 +13,6 @@ import javax.ws.rs.core.Response; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH; import static meerkat.bulletinboard.BulletinBoardConstants.READ_MESSAGES_PATH; diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java new file mode 100644 index 0000000..a3ace28 --- /dev/null +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java @@ -0,0 +1,241 @@ +package meerkat.bulletinboard; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.protobuf.ByteString; + +import static meerkat.bulletinboard.BulletinBoardSynchronizer.SyncStatus; + +import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; +import meerkat.bulletinboard.sqlserver.H2QueryProvider; + +import meerkat.comm.CommunicationException; +import meerkat.crypto.concrete.ECDSASignature; +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.util.BulletinBoardMessageComparator; +import meerkat.util.BulletinBoardMessageGenerator; +import org.junit.*; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; + +import java.io.IOException; +import java.io.InputStream; +import java.security.*; +import java.security.cert.CertificateException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.Semaphore; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.fail; + +/** + * Created by Arbel on 6/1/2016. + */ +public class BulletinBoardSynchronizerTest { + + private static final String REMOTE_SERVER_ADDRESS = "remoteDB"; + private static final String LOCAL_SERVER_ADDRESS = "localDB"; + + private static final int THREAD_NUM = 3; + private static final int SUBSCRIPTION_INTERVAL = 1000; + + private DeletableSubscriptionBulletinBoardClient localClient; + private AsyncBulletinBoardClient remoteClient; + + private BulletinBoardSynchronizer synchronizer; + + private static BulletinBoardMessageGenerator messageGenerator; + private static BulletinBoardMessageComparator messageComparator; + + private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12"; + private static String KEYFILE_PASSWORD1 = "secret"; + private static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt"; + + private static GenericBatchDigitalSignature[] signers; + private static ByteString[] signerIDs; + + private Semaphore semaphore; + private List thrown; + + @BeforeClass + public static void build() { + + messageGenerator = new BulletinBoardMessageGenerator(new Random(0)); + messageComparator = new BulletinBoardMessageComparator(); + + signers = new GenericBatchDigitalSignature[1]; + signerIDs = new ByteString[1]; + + signers[0] = new GenericBatchDigitalSignature(new ECDSASignature()); + + InputStream keyStream = BulletinBoardSynchronizerTest.class.getResourceAsStream(KEYFILE_EXAMPLE); + char[] password = KEYFILE_PASSWORD1.toCharArray(); + + try { + + KeyStore.Builder keyStoreBuilder = signers[0].getPKCS12KeyStoreBuilder(keyStream, password); + + signers[0].loadSigningCertificate(keyStoreBuilder); + + signers[0].loadVerificationCertificates(BulletinBoardSynchronizerTest.class.getResourceAsStream(CERT1_PEM_EXAMPLE)); + + } catch (IOException e) { + System.err.println("Failed reading from signature file " + e.getMessage()); + fail("Failed reading from signature file " + e.getMessage()); + } catch (CertificateException e) { + System.err.println("Failed reading certificate " + e.getMessage()); + fail("Failed reading certificate " + e.getMessage()); + } catch (KeyStoreException e) { + System.err.println("Failed reading keystore " + e.getMessage()); + fail("Failed reading keystore " + e.getMessage()); + } catch (NoSuchAlgorithmException e) { + System.err.println("Couldn't find signing algorithm " + e.getMessage()); + fail("Couldn't find signing algorithm " + e.getMessage()); + } catch (UnrecoverableKeyException e) { + System.err.println("Couldn't find signing key " + e.getMessage()); + fail("Couldn't find signing key " + e.getMessage()); + } + + signerIDs[0] = signers[0].getSignerID(); + + } + + @Before + public void init() throws CommunicationException { + + DeletableBulletinBoardServer remoteServer = new BulletinBoardSQLServer(new H2QueryProvider(REMOTE_SERVER_ADDRESS)); + remoteServer.init(REMOTE_SERVER_ADDRESS); + + remoteClient = new LocalBulletinBoardClient( + remoteServer, + THREAD_NUM, + SUBSCRIPTION_INTERVAL); + + DeletableBulletinBoardServer localServer = new BulletinBoardSQLServer(new H2QueryProvider(LOCAL_SERVER_ADDRESS)); + localServer.init(LOCAL_SERVER_ADDRESS); + + localClient = new LocalBulletinBoardClient( + localServer, + THREAD_NUM, + SUBSCRIPTION_INTERVAL); + + synchronizer = new SimpleBulletinBoardSynchronizer(); + synchronizer.init(localClient, remoteClient); + + semaphore = new Semaphore(0); + thrown = new LinkedList<>(); + + } + + private class SyncStatusCallback implements FutureCallback { + + @Override + public void onSuccess(SyncStatus result) { + + if (result == SyncStatus.SYNCHRONIZED){ + semaphore.release(); + } + + } + + @Override + public void onFailure(Throwable t) { + thrown.add(t); + semaphore.release(); + } + } + + private class MessageCountCallback implements FutureCallback { + + private int[] expectedCounts; + private int currentIteration; + + public MessageCountCallback(int[] expectedCounts) { + this.expectedCounts = expectedCounts; + this.currentIteration = 0; + } + + @Override + public void onSuccess(Integer result) { + + if (currentIteration < expectedCounts.length){ + if (result != expectedCounts[currentIteration]){ + onFailure(new AssertionError("Wrong message count. Expected " + expectedCounts[currentIteration] + " but received " + result)); + currentIteration = expectedCounts.length; + return; + } + } + + currentIteration++; + + if (currentIteration == expectedCounts.length) + semaphore.release(); + } + + @Override + public void onFailure(Throwable t) { + thrown.add(t); + semaphore.release(); + } + } + + @Test + public void testSync() throws SignatureException, CommunicationException, InterruptedException { + + final int BATCH_ID = 1; + + BulletinBoardMessage msg = messageGenerator.generateRandomMessage(signers, 10, 10); + + MessageID msgID = localClient.postMessage(msg); + + CompleteBatch completeBatch = messageGenerator.generateRandomBatch(signers[0],BATCH_ID,10,10,10); + + localClient.postBatch(completeBatch); + + synchronizer.subscribeToSyncStatus(new SyncStatusCallback()); + + int[] expectedCounts = {2,0}; + synchronizer.subscribeToRemainingMessagesCount(new MessageCountCallback(expectedCounts)); + + Thread t = new Thread(synchronizer); + t.run(); + + semaphore.acquire(); + + synchronizer.stop(); + t.join(); + + assertThat("Exception thrown by Synchronizer: " + thrown.get(0).getMessage(), thrown.size() == 0); + + List msgList = remoteClient.readMessages(MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.MSG_ID) + .setId(msgID.getID()) + .build()) + .build()); + + assertThat("Wrong number of messages returned.", msgList.size() == 1); + assertThat("Returned message is not equal to original one", messageComparator.compare(msgList.get(0),msg) == 0); + + CompleteBatch returnedBatch = remoteClient.readBatch(BatchSpecificationMessage.newBuilder() + .setSignerId(signerIDs[0]) + .setBatchId(BATCH_ID) + .build()); + + assertThat("Returned batch does not equal original one.", completeBatch.equals(returnedBatch)); + + } + + @After + public void close() { + + synchronizer.stop(); + localClient.close(); + remoteClient.close(); + + } + +} diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java index 6b2ef4f..301fc8e 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java @@ -6,7 +6,6 @@ import java.util.*; import com.google.protobuf.*; import com.google.protobuf.Timestamp; -import com.sun.org.apache.xpath.internal.operations.Bool; import meerkat.bulletinboard.*; import meerkat.bulletinboard.sqlserver.mappers.*; import static meerkat.bulletinboard.BulletinBoardConstants.*; @@ -17,7 +16,6 @@ import meerkat.comm.MessageOutputStream; import meerkat.crypto.concrete.ECDSASignature; import meerkat.crypto.concrete.SHA256Digest; -import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.protobuf.Crypto.Signature; import meerkat.protobuf.Crypto.SignatureVerificationKey; @@ -29,7 +27,6 @@ import javax.sql.DataSource; import meerkat.util.BulletinBoardUtils; import meerkat.util.TimestampComparator; -import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.support.GeneratedKeyHolder; diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java index bef6d68..f68548e 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java @@ -247,12 +247,12 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INT NOT NULL AUTO_INCREMENT PRIMARY KEY, Tag VARCHAR(50) UNIQUE)"); list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum INT, TagId INT," - + " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum)," - + " FOREIGN KEY (TagId) REFERENCES TagTable(TagId)," + + " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE," + + " FOREIGN KEY (TagId) REFERENCES TagTable(TagId) ON DELETE CASCADE," + " UNIQUE (EntryNum, TagID))"); list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INT, SignerId TINYBLOB, Signature TINYBLOB UNIQUE," - + " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))"); + + " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE)"); list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)"); list.add("CREATE UNIQUE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId, EntryNum)"); @@ -261,7 +261,7 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider + " UNIQUE(SignerId, BatchId, SerialNum))"); list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, TagId INT," - + " FOREIGN KEY (TagId) REFERENCES TagTable(TagId))"); + + " FOREIGN KEY (TagId) REFERENCES TagTable(TagId) ON DELETE CASCADE)"); list.add("CREATE INDEX IF NOT EXISTS BatchIndex ON BatchTagTable(SignerId, BatchId)"); diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSynchronizer.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSynchronizer.java index 569d4ef..c25d737 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSynchronizer.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSynchronizer.java @@ -16,8 +16,8 @@ public interface BulletinBoardSynchronizer extends Runnable { public enum SyncStatus{ SYNCHRONIZED, // No more messages to upload - PENDING, // Synchronizer is uploading data - SERVER_ERROR, // Synchronizer encountered an error while uploading + PENDING, // Synchronizer is querying for data to upload and uploading it as needed + SERVER_ERROR, // Synchronizer encountered an error while uploading, but will retry STOPPED // Stopped/Not started by user } diff --git a/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageGenerator.java b/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageGenerator.java index dff562e..ea25200 100644 --- a/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageGenerator.java +++ b/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageGenerator.java @@ -1,13 +1,14 @@ package meerkat.util; import com.google.protobuf.ByteString; +import meerkat.bulletinboard.BatchDigitalSignature; +import meerkat.bulletinboard.CompleteBatch; import meerkat.crypto.DigitalSignature; import meerkat.protobuf.BulletinBoardAPI.*; import com.google.protobuf.Timestamp; import java.math.BigInteger; import java.security.SignatureException; -import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Random; @@ -28,10 +29,33 @@ public class BulletinBoardMessageGenerator { return (byte) random.nextInt(); } + private byte[] randomBytes(int length) { + + byte[] result = new byte[length]; + + for (int i = 0; i < length; i++) { + result[i] = randomByte(); + } + + return result; + } + private String randomString(){ return new BigInteger(130, random).toString(32); } + private List randomStrings(int length) { + + List result = new LinkedList<>(); + + for (int i = 0; i < length; i++) { + result.add(randomString()); + } + + return result; + + } + /** * Generates a complete instance of a BulletinBoardMessage * @param signers contains the (possibly multiple) credentials required to sign the message @@ -46,23 +70,16 @@ public class BulletinBoardMessageGenerator { // Generate random data. - byte[] data = new byte[dataSize]; - String[] newTags = new String[tagNumber]; - for (int i = 0; i < dataSize; i++) { - data[i] = randomByte(); - } - for (int i = 0; i < tagNumber; i++) { - newTags[i] = randomString(); - } + UnsignedBulletinBoardMessage unsignedMessage = UnsignedBulletinBoardMessage.newBuilder() - .setData(ByteString.copyFrom(data)) + .setData(ByteString.copyFrom(randomBytes(dataSize))) .setTimestamp(timestamp) .addAllTag(tags) - .addAllTag(Arrays.asList(newTags)) + .addAllTag(randomStrings(tagNumber)) .build(); BulletinBoardMessage.Builder messageBuilder = @@ -102,7 +119,6 @@ public class BulletinBoardMessageGenerator { * @param tagNumber is the number of tags to generate * @return a random, signed Bulletin Board Message containing random data, tags and timestamp */ - public BulletinBoardMessage generateRandomMessage(DigitalSignature[] signers, int dataSize, int tagNumber) throws SignatureException { @@ -115,4 +131,97 @@ public class BulletinBoardMessageGenerator { } + /** + * Generates a complete instance of a CompleteBatch + * @param signer contains the credentials required to sign the message + * @param batchId is the (per-signer) batch-ID + * @param timestamp is the time at which the message was generated + * @param dataCount is the number of Batch Data in the batch + * @param dataSize is the number of bytes per Batch Data + * @param tagCount is the number of tags + * @param tags contains a list of tags to be added (in addition to the random ones) + * @return a random, signed CompleteBatch containing random data and tags + * @throws SignatureException if an error occurs while signing the batch + */ + public CompleteBatch generateRandomBatch(BatchDigitalSignature signer, int batchId, Timestamp timestamp, int dataCount, int dataSize, int tagCount, List tags) throws SignatureException { + + CompleteBatch result = new CompleteBatch(BeginBatchMessage.newBuilder() + .setSignerId(signer.getSignerID()) + .setBatchId(batchId) + .addAllTag(tags) + .addAllTag(randomStrings(tagCount)) + .build()); + + List batchDataList = new LinkedList<>(); + + for (int i = 0 ; i < dataCount ; i++) { + batchDataList.add(BatchData.newBuilder() + .setData(ByteString.copyFrom(randomBytes(dataSize))) + .build()); + } + + result.appendBatchData(batchDataList); + + result.setTimestamp(timestamp); + + signer.updateContent(result); + + result.setSignature(signer.sign()); + + return result; + + } + + /** + * Generates a complete instance of a CompleteBatch + * @param signer contains the credentials required to sign the message + * @param batchId is the (per-signer) batch-ID + * @param timestamp is the time at which the message was generated + * @param dataCount is the number of Batch Data in the batch + * @param dataSize is the number of bytes per Batch Data + * @param tagCount is the number of tags + * @return a random, signed CompleteBatch containing random data and tags + * @throws SignatureException if an error occurs while signing the batch + */ + public CompleteBatch generateRandomBatch(BatchDigitalSignature signer, int batchId, Timestamp timestamp, int dataCount, int dataSize, int tagCount) throws SignatureException { + return generateRandomBatch(signer, batchId, timestamp, dataCount, dataSize, tagCount, new LinkedList()); + } + + + /** + * Generates a complete instance of a CompleteBatch + * @param signer contains the credentials required to sign the message + * @param batchId is the (per-signer) batch-ID + * @param dataCount is the number of Batch Data in the batch + * @param dataSize is the number of bytes per Batch Data + * @param tagCount is the number of tags + * @param tags contains a list of tags to be added (in addition to the random ones) + * @return a random, signed CompleteBatch containing random data, tags and timestamp + * @throws SignatureException if an error occurs while signing the batch + */ + public CompleteBatch generateRandomBatch(BatchDigitalSignature signer, int batchId, int dataCount, int dataSize, int tagCount, List tags) throws SignatureException { + + Timestamp timestamp = Timestamp.newBuilder() + .setSeconds(random.nextLong()) + .setNanos(random.nextInt()) + .build(); + + return generateRandomBatch(signer, batchId, timestamp, dataCount, dataSize, tagCount, tags); + + } + + /** + * Generates a complete instance of a CompleteBatch + * @param signer contains the credentials required to sign the message + * @param batchId is the (per-signer) batch-ID + * @param dataCount is the number of Batch Data in the batch + * @param dataSize is the number of bytes per Batch Data + * @param tagCount is the number of tags + * @return a random, signed CompleteBatch containing random data, tags and timestamp + * @throws SignatureException if an error occurs while signing the batch + */ + public CompleteBatch generateRandomBatch(BatchDigitalSignature signer, int batchId, int dataCount, int dataSize, int tagCount) throws SignatureException { + return generateRandomBatch(signer, batchId, dataCount, dataSize, tagCount, new LinkedList()); + } + } From e91a48b5e1d63c8e38a10fd9cf68607f5e2738e6 Mon Sep 17 00:00:00 2001 From: Arbel Deutsch Peled Date: Wed, 1 Jun 2016 22:46:51 +0300 Subject: [PATCH 05/15] Fixed a few bugs. Changed H2 Query Provider to run in-memory. --- .../LocalBulletinBoardClient.java | 2 +- .../SimpleBulletinBoardSynchronizer.java | 23 +++++++++++-------- .../BulletinBoardSynchronizerTest.java | 20 +++++++++++----- .../GenericSubscriptionClientTester.java | 6 +---- .../sqlserver/H2QueryProvider.java | 2 +- 5 files changed, 31 insertions(+), 22 deletions(-) diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java index 109d5ed..8e2284c 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java @@ -500,7 +500,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo MessagePoster poster = new MessagePoster(msg); poster.call(); - digest.update(msg); + digest.update(msg.getMsg()); return digest.digestAsMessageID(); } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java index f7e9baa..97b6d01 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java @@ -26,8 +26,11 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize private List> syncStatusCallbacks; private static final MessageFilterList EMPTY_FILTER = MessageFilterList.getDefaultInstance(); - private static final int SLEEP_INTERVAL = 10000; // 10 Seconds - private static final int WAIT_CAP = 300000; // 5 minutes wait before deciding that the sync has failed fatally + private static final int DEFAULT_SLEEP_INTERVAL = 10000; // 10 Seconds + private static final int DEFAULT_WAIT_CAP = 300000; // 5 minutes wait before deciding that the sync has failed fatally + + private final int SLEEP_INTERVAL; + private final int WAIT_CAP; private Semaphore semaphore; @@ -123,6 +126,7 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize if (result.size() == 0) { newStatus = SyncStatus.SYNCHRONIZED; + semaphore.release(); } else{ // Upload messages @@ -181,8 +185,14 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize } - public SimpleBulletinBoardSynchronizer() { + public SimpleBulletinBoardSynchronizer(int sleepInterval, int waitCap) { this.syncStatus = SyncStatus.STOPPED; + this.SLEEP_INTERVAL = sleepInterval; + this.WAIT_CAP = waitCap; + } + + public SimpleBulletinBoardSynchronizer() { + this(DEFAULT_SLEEP_INTERVAL, DEFAULT_WAIT_CAP); } private synchronized void updateSyncStatus(SyncStatus newStatus) { @@ -257,11 +267,7 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize do { - if (syncStatus == SyncStatus.PENDING || syncStatus == SyncStatus.SERVER_ERROR) { - - localClient.readMessages(EMPTY_FILTER, callback); - - } + localClient.readMessages(EMPTY_FILTER, callback); try { @@ -272,7 +278,6 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize // We expect an interruption when the upload will complete } - } while (syncStatus == SyncStatus.PENDING); // Database is synced. Wait for new data. diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java index a3ace28..1813641 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java @@ -42,6 +42,9 @@ public class BulletinBoardSynchronizerTest { private static final int THREAD_NUM = 3; private static final int SUBSCRIPTION_INTERVAL = 1000; + private static final int SYNC_SLEEP_INTERVAL = 100; + private static final int SYNC_WAIT_CAP = 200; + private DeletableSubscriptionBulletinBoardClient localClient; private AsyncBulletinBoardClient remoteClient; @@ -70,6 +73,7 @@ public class BulletinBoardSynchronizerTest { signerIDs = new ByteString[1]; signers[0] = new GenericBatchDigitalSignature(new ECDSASignature()); + signerIDs[0] = signers[0].getSignerID(); InputStream keyStream = BulletinBoardSynchronizerTest.class.getResourceAsStream(KEYFILE_EXAMPLE); char[] password = KEYFILE_PASSWORD1.toCharArray(); @@ -122,7 +126,7 @@ public class BulletinBoardSynchronizerTest { THREAD_NUM, SUBSCRIPTION_INTERVAL); - synchronizer = new SimpleBulletinBoardSynchronizer(); + synchronizer = new SimpleBulletinBoardSynchronizer(SYNC_SLEEP_INTERVAL, SYNC_WAIT_CAP); synchronizer.init(localClient, remoteClient); semaphore = new Semaphore(0); @@ -200,15 +204,19 @@ public class BulletinBoardSynchronizerTest { int[] expectedCounts = {2,0}; synchronizer.subscribeToRemainingMessagesCount(new MessageCountCallback(expectedCounts)); - Thread t = new Thread(synchronizer); - t.run(); + Thread syncThread = new Thread(synchronizer); + syncThread.start(); - semaphore.acquire(); + semaphore.acquire(2); synchronizer.stop(); - t.join(); + syncThread.join(); - assertThat("Exception thrown by Synchronizer: " + thrown.get(0).getMessage(), thrown.size() == 0); + if (thrown.size() > 0) { + for (Throwable t : thrown) + System.err.println(t.getMessage()); + assertThat("Exception thrown by Synchronizer: " + thrown.get(0).getMessage(), false); + } List msgList = remoteClient.readMessages(MessageFilterList.newBuilder() .addFilter(MessageFilter.newBuilder() diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java index 4a5fe62..52da797 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java @@ -16,8 +16,6 @@ import java.security.cert.CertificateException; import java.util.*; import java.util.concurrent.Semaphore; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** @@ -187,10 +185,8 @@ public class GenericSubscriptionClientTester { public void subscriptionTest() throws SignatureException, CommunicationException { - final int FIRST_POST_ID = 201; - final int SECOND_POST_ID = 202; final String COMMON_TAG = "SUBSCRIPTION_TEST"; - + List tags = new LinkedList<>(); tags.add(COMMON_TAG); diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java index f68548e..55faa8f 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java @@ -231,7 +231,7 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName("org.h2.Driver"); - dataSource.setUrl("jdbc:h2:~/" + dbName); + dataSource.setUrl("jdbc:h2:mem:" + dbName); return dataSource; From e2f3dbe6b2f615012e3365839369cf0a1e6b3e33 Mon Sep 17 00:00:00 2001 From: "arbel.peled" Date: Thu, 2 Jun 2016 10:38:31 +0300 Subject: [PATCH 06/15] Fixed some more issues (most have to do with concurrency). Implemented close method for the SQLServer which renders it unusable until reinitialization. Added test for Synchronizer for the case when the remote server is unavailable (test passes). Still need to fix Batch digest and sign issue. --- .../LocalBulletinBoardClient.java | 7 +- .../SimpleBulletinBoardSynchronizer.java | 14 +++- .../BulletinBoardSynchronizerTest.java | 70 +++++++++++++++---- .../sqlserver/BulletinBoardSQLServer.java | 4 +- 4 files changed, 75 insertions(+), 20 deletions(-) diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java index 8e2284c..f944214 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java @@ -32,13 +32,12 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo private final DeletableBulletinBoardServer server; private final ListeningScheduledExecutorService executorService; private final BatchDigest digest; - private final int subsrciptionDelay; + private final long subsrciptionDelay; /** * Initializes an instance of the client * @param server an initialized Bulletin Board Server instance which will perform the actual processing of the requests * @param threadNum is the number of concurrent threads to allocate for the client - * @param subscriptionDelay is the required delay between subscription calls in milliseconds */ public LocalBulletinBoardClient(DeletableBulletinBoardServer server, int threadNum, int subscriptionDelay) { this.server = server; @@ -108,7 +107,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo @Override public MessageID postBatch(CompleteBatch completeBatch, FutureCallback callback) { - Futures.addCallback(executorService.schedule(new CompleteBatchPoster(completeBatch), subsrciptionDelay, TimeUnit.MILLISECONDS), callback); + Futures.addCallback(executorService.submit(new CompleteBatchPoster(completeBatch)), callback); digest.update(completeBatch); return digest.digestAsMessageID(); @@ -353,7 +352,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo filterList = filterBuilder.build(); // Reschedule job - Futures.addCallback(executorService.submit(new MessageReader(filterList)), this); + Futures.addCallback(executorService.schedule(new MessageReader(filterList), subsrciptionDelay, TimeUnit.MILLISECONDS), this); } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java index 97b6d01..7700fd6 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java @@ -10,6 +10,7 @@ import java.util.LinkedList; import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; /** * Created by Arbel on 13/04/2016. @@ -20,6 +21,7 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize private DeletableSubscriptionBulletinBoardClient localClient; private AsyncBulletinBoardClient remoteClient; + private AtomicBoolean running; private volatile SyncStatus syncStatus; private List> messageCountCallbacks; @@ -160,8 +162,6 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize } - localClient.deleteMessage(message.getEntryNum()); - } catch (CommunicationException e) { // This is an error with the local server // TODO: log @@ -189,6 +189,7 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize this.syncStatus = SyncStatus.STOPPED; this.SLEEP_INTERVAL = sleepInterval; this.WAIT_CAP = waitCap; + this.running = new AtomicBoolean(false); } public SimpleBulletinBoardSynchronizer() { @@ -197,6 +198,12 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize private synchronized void updateSyncStatus(SyncStatus newStatus) { + if (!running.get()) { + + newStatus = SyncStatus.STOPPED; + + } + if (newStatus != syncStatus){ syncStatus = newStatus; @@ -258,7 +265,7 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize @Override public void run() { - if (syncStatus == SyncStatus.STOPPED) { + if (running.compareAndSet(false,true)){ updateSyncStatus(SyncStatus.PENDING); SyncCallback callback = new SyncCallback(); @@ -302,6 +309,7 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize @Override public void stop() { + running.set(false); updateSyncStatus(SyncStatus.STOPPED); } diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java index 1813641..d412502 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java @@ -14,19 +14,18 @@ import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.util.BulletinBoardMessageComparator; import meerkat.util.BulletinBoardMessageGenerator; import org.junit.*; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import java.io.IOException; import java.io.InputStream; import java.security.*; import java.security.cert.CertificateException; -import java.sql.ResultSet; -import java.sql.SQLException; import java.util.LinkedList; import java.util.List; import java.util.Random; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; @@ -38,12 +37,13 @@ public class BulletinBoardSynchronizerTest { private static final String REMOTE_SERVER_ADDRESS = "remoteDB"; private static final String LOCAL_SERVER_ADDRESS = "localDB"; + private static int testCount; private static final int THREAD_NUM = 3; private static final int SUBSCRIPTION_INTERVAL = 1000; - private static final int SYNC_SLEEP_INTERVAL = 100; - private static final int SYNC_WAIT_CAP = 200; + private static final int SYNC_SLEEP_INTERVAL = 500; + private static final int SYNC_WAIT_CAP = 1000; private DeletableSubscriptionBulletinBoardClient localClient; private AsyncBulletinBoardClient remoteClient; @@ -105,12 +105,14 @@ public class BulletinBoardSynchronizerTest { signerIDs[0] = signers[0].getSignerID(); + testCount = 0; + } @Before public void init() throws CommunicationException { - DeletableBulletinBoardServer remoteServer = new BulletinBoardSQLServer(new H2QueryProvider(REMOTE_SERVER_ADDRESS)); + DeletableBulletinBoardServer remoteServer = new BulletinBoardSQLServer(new H2QueryProvider(REMOTE_SERVER_ADDRESS + testCount)); remoteServer.init(REMOTE_SERVER_ADDRESS); remoteClient = new LocalBulletinBoardClient( @@ -118,7 +120,7 @@ public class BulletinBoardSynchronizerTest { THREAD_NUM, SUBSCRIPTION_INTERVAL); - DeletableBulletinBoardServer localServer = new BulletinBoardSQLServer(new H2QueryProvider(LOCAL_SERVER_ADDRESS)); + DeletableBulletinBoardServer localServer = new BulletinBoardSQLServer(new H2QueryProvider(LOCAL_SERVER_ADDRESS + testCount)); localServer.init(LOCAL_SERVER_ADDRESS); localClient = new LocalBulletinBoardClient( @@ -132,14 +134,24 @@ public class BulletinBoardSynchronizerTest { semaphore = new Semaphore(0); thrown = new LinkedList<>(); + testCount++; + } private class SyncStatusCallback implements FutureCallback { + private final SyncStatus statusToWaitFor; + private AtomicBoolean stillWaiting; + + public SyncStatusCallback(SyncStatus statusToWaitFor) { + this.statusToWaitFor = statusToWaitFor; + stillWaiting = new AtomicBoolean(true); + } + @Override public void onSuccess(SyncStatus result) { - if (result == SyncStatus.SYNCHRONIZED){ + if (result == statusToWaitFor && stillWaiting.compareAndSet(true, false)){ semaphore.release(); } @@ -148,7 +160,9 @@ public class BulletinBoardSynchronizerTest { @Override public void onFailure(Throwable t) { thrown.add(t); - semaphore.release(); + if (stillWaiting.compareAndSet(true,false)) { + semaphore.release(); + } } } @@ -199,7 +213,7 @@ public class BulletinBoardSynchronizerTest { localClient.postBatch(completeBatch); - synchronizer.subscribeToSyncStatus(new SyncStatusCallback()); + synchronizer.subscribeToSyncStatus(new SyncStatusCallback(SyncStatus.SYNCHRONIZED)); int[] expectedCounts = {2,0}; synchronizer.subscribeToRemainingMessagesCount(new MessageCountCallback(expectedCounts)); @@ -207,7 +221,9 @@ public class BulletinBoardSynchronizerTest { Thread syncThread = new Thread(synchronizer); syncThread.start(); - semaphore.acquire(2); + if (!semaphore.tryAcquire(2, 4000, TimeUnit.MILLISECONDS)) { + thrown.add(new TimeoutException("Timeout occurred while waiting for synchronizer to sync.")); + } synchronizer.stop(); syncThread.join(); @@ -237,6 +253,36 @@ public class BulletinBoardSynchronizerTest { } + @Test + public void testServerError() throws SignatureException, CommunicationException, InterruptedException { + + BulletinBoardMessage msg = messageGenerator.generateRandomMessage(signers, 10, 10); + + remoteClient.close(); + + synchronizer.subscribeToSyncStatus(new SyncStatusCallback(SyncStatus.SERVER_ERROR)); + + localClient.postMessage(msg); + + Thread thread = new Thread(synchronizer); + + thread.start(); + + if (!semaphore.tryAcquire(4000, TimeUnit.MILLISECONDS)) { + thrown.add(new TimeoutException("Timeout occurred while waiting for synchronizer to sync.")); + } + + synchronizer.stop(); + thread.join(); + + if (thrown.size() > 0) { + for (Throwable t : thrown) + System.err.println(t.getMessage()); + assertThat("Exception thrown by Synchronizer: " + thrown.get(0).getMessage(), false); + } + + } + @After public void close() { diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java index 301fc8e..d331c53 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java @@ -1090,6 +1090,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ @Override - public void close() {} + public void close() { + jdbcTemplate = null; + } } From fe209f6b5a0cd95d5b8fa451ecfc68deed18d073 Mon Sep 17 00:00:00 2001 From: "arbel.peled" Date: Thu, 2 Jun 2016 10:39:29 +0300 Subject: [PATCH 07/15] Removed default testing for the Bulletin Board Client. --- bulletin-board-client/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bulletin-board-client/build.gradle b/bulletin-board-client/build.gradle index 8fdeba0..03c395a 100644 --- a/bulletin-board-client/build.gradle +++ b/bulletin-board-client/build.gradle @@ -69,7 +69,7 @@ dependencies { test { exclude '**/*IntegrationTest*' - outputs.upToDateWhen { false } +// outputs.upToDateWhen { false } } task integrationTest(type: Test) { From 229cbfd48f8e19dc5ef33336f26208cbb8a9d607 Mon Sep 17 00:00:00 2001 From: "arbel.peled" Date: Thu, 2 Jun 2016 13:01:41 +0300 Subject: [PATCH 08/15] Fixed some subscription functionality of the CachedClient --- .../CachedBulletinBoardClient.java | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java index ac61114..15beec7 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java @@ -5,6 +5,7 @@ import com.google.protobuf.ByteString; import meerkat.comm.CommunicationException; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.protobuf.Voting.*; +import meerkat.util.BulletinBoardUtils; import java.util.List; @@ -30,13 +31,13 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien private class SubscriptionStoreCallback implements FutureCallback> { - private final FutureCallback callback; + private final FutureCallback> callback; public SubscriptionStoreCallback(){ callback = null; } - public SubscriptionStoreCallback(FutureCallback callback){ + public SubscriptionStoreCallback(FutureCallback> callback){ this.callback = callback; } @@ -44,7 +45,30 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien public void onSuccess(List result) { for (BulletinBoardMessage msg : result) { try { - localClient.postMessage(msg); + + if (msg.getMsg().getTagList().contains(BulletinBoardConstants.BATCH_TAG)) { + + // This is a batch message: need to upload batch data as well as the message itself + ByteString signerID = msg.getSig(0).getSignerId(); + int batchID = Integer.parseInt(BulletinBoardUtils.findTagWithPrefix(msg, BulletinBoardConstants.BATCH_ID_TAG_PREFIX)); + + BatchSpecificationMessage batchSpecificationMessage = BatchSpecificationMessage.newBuilder() + .setSignerId(signerID) + .setBatchId(batchID) + .setStartPosition(0) + .build(); + + CompleteBatch completeBatch = localClient.readBatch(batchSpecificationMessage); + + localClient.postBatch(completeBatch); + + } else { + + // This is a regular message: post it + localClient.postMessage(msg); + + } + } catch (CommunicationException ignored) { // TODO: log } @@ -346,16 +370,21 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien localClient.close(); remoteClient.close(); synchronizer.stop(); + try { + syncThread.join(); + } catch (InterruptedException e) { + //TODO: log interruption + } } @Override public void subscribe(MessageFilterList filterList, FutureCallback> callback) { - subscriber.subscribe(filterList, callback); + subscriber.subscribe(filterList, new SubscriptionStoreCallback(callback)); } @Override public void subscribe(MessageFilterList filterList, long startEntry, FutureCallback> callback) { - subscriber.subscribe(filterList, startEntry, callback); + subscriber.subscribe(filterList, startEntry, new SubscriptionStoreCallback(callback)); } } \ No newline at end of file From 337a135151dfb75c2a9dc609d7986e4000e53e2b Mon Sep 17 00:00:00 2001 From: Arbel Deutsch Peled Date: Wed, 15 Jun 2016 10:34:46 +0300 Subject: [PATCH 09/15] Started removing dependency on CompleteBatch. Tags of batches are now stored as a blob until the batch is complete. --- .../LocalBulletinBoardClient.java | 13 +-- .../SimpleBulletinBoardClient.java | 4 +- .../ThreadedBulletinBoardClient.java | 10 +-- .../MultiServerPostBatchWorker.java | 7 +- .../MultiServerReadBatchWorker.java | 6 +- .../sqlserver/BulletinBoardSQLServer.java | 90 ++++++------------- .../sqlserver/H2QueryProvider.java | 21 ++--- .../mappers/BeginBatchMessageMapper.java | 24 +++++ .../proto/meerkat/bulletin_board_server.proto | 9 -- .../AsyncBulletinBoardClient.java | 16 +++- .../meerkat/bulletinboard/BatchDigest.java | 20 ----- .../bulletinboard/BulletinBoardDigest.java | 42 +++++++++ .../bulletinboard/GenericBatchDigest.java | 61 ------------- .../GenericBulletinBoardDigest.java | 79 ++++++++++++++++ .../src/main/java/meerkat/crypto/Digest.java | 6 ++ .../meerkat/crypto/concrete/SHA256Digest.java | 1 + 16 files changed, 223 insertions(+), 186 deletions(-) create mode 100644 bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BeginBatchMessageMapper.java delete mode 100644 bulletin-board-server/src/main/proto/meerkat/bulletin_board_server.proto delete mode 100644 meerkat-common/src/main/java/meerkat/bulletinboard/BatchDigest.java create mode 100644 meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardDigest.java delete mode 100644 meerkat-common/src/main/java/meerkat/bulletinboard/GenericBatchDigest.java create mode 100644 meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardDigest.java diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java index f944214..0bc03ee 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java @@ -31,7 +31,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo private final DeletableBulletinBoardServer server; private final ListeningScheduledExecutorService executorService; - private final BatchDigest digest; + private final BulletinBoardDigest digest; private final long subsrciptionDelay; /** @@ -42,7 +42,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo public LocalBulletinBoardClient(DeletableBulletinBoardServer server, int threadNum, int subscriptionDelay) { this.server = server; this.executorService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(threadNum)); - this.digest = new GenericBatchDigest(new SHA256Digest()); + this.digest = new GenericBulletinBoardDigest(new SHA256Digest()); this.subsrciptionDelay = subscriptionDelay; } @@ -74,9 +74,9 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo private class CompleteBatchPoster implements Callable { - private final CompleteBatch completeBatch; + private final BulletinBoardMessage completeBatch; - public CompleteBatchPoster(CompleteBatch completeBatch) { + public CompleteBatchPoster(BulletinBoardMessage completeBatch) { this.completeBatch = completeBatch; } @@ -84,7 +84,8 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo @Override public Boolean call() throws CommunicationException { - server.beginBatch(completeBatch.getBeginBatchMessage()); + server.beginBatch(BeginBatchMessage.newBuilder().setSignerId(completeBatch.getSig(0)) + completeBatch.getBeginBatchMessage()); BatchMessage.Builder builder = BatchMessage.newBuilder() .setSignerId(completeBatch.getSignature().getSignerId()) @@ -105,7 +106,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo } @Override - public MessageID postBatch(CompleteBatch completeBatch, FutureCallback callback) { + public MessageID postBatch(ByteString signerId, int batchId, BulletinBoardMessage completeBatch, FutureCallback callback) { Futures.addCallback(executorService.submit(new CompleteBatchPoster(completeBatch)), callback); diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java index 579ecf9..e8fcea0 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java @@ -28,7 +28,7 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{ protected Client client; - protected BatchDigest digest; + protected BulletinBoardDigest digest; /** * Stores database locations and initializes the web Client @@ -44,7 +44,7 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{ client.register(ProtobufMessageBodyWriter.class); // Wrap the Digest into a BatchDigest - digest = new GenericBatchDigest(new SHA256Digest()); + digest = new GenericBulletinBoardDigest(new SHA256Digest()); } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java index 52e28e0..c53afe7 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java @@ -4,8 +4,6 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.protobuf.ByteString; import meerkat.bulletinboard.workers.multiserver.*; -import meerkat.comm.CommunicationException; -import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.protobuf.Voting.*; @@ -31,7 +29,7 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple // Per-server clients private List clients; - private BatchDigest batchDigest; + private BulletinBoardDigest batchDigest; private final static int POST_MESSAGE_RETRY_NUM = 3; private final static int READ_MESSAGES_RETRY_NUM = 1; @@ -54,7 +52,7 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple super.init(clientParams); - batchDigest = new GenericBatchDigest(digest); + batchDigest = new GenericBulletinBoardDigest(digest); minAbsoluteRedundancy = (int) (clientParams.getMinRedundancy() * (float) clientParams.getBulletinBoardAddressCount()); @@ -100,7 +98,7 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple } @Override - public MessageID postBatch(CompleteBatch completeBatch, FutureCallback callback) { + public MessageID postBatch(BulletinBoardMessage completeBatch, FutureCallback callback) { // Create job MultiServerPostBatchWorker worker = @@ -213,7 +211,7 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple } @Override - public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback callback) { + public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback callback) { // Create job MultiServerReadBatchWorker worker = diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchWorker.java index 1b1f3df..763485f 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchWorker.java @@ -3,16 +3,17 @@ package meerkat.bulletinboard.workers.multiserver; import com.google.common.util.concurrent.FutureCallback; import meerkat.bulletinboard.CompleteBatch; import meerkat.bulletinboard.SingleServerBulletinBoardClient; +import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage; import java.util.List; /** * Created by Arbel Deutsch Peled on 27-Dec-15. */ -public class MultiServerPostBatchWorker extends MultiServerGenericPostWorker { +public class MultiServerPostBatchWorker extends MultiServerGenericPostWorker { public MultiServerPostBatchWorker(List clients, - int minServers, CompleteBatch payload, int maxRetry, + int minServers, BulletinBoardMessage payload, int maxRetry, FutureCallback futureCallback) { super(clients, minServers, payload, maxRetry, futureCallback); @@ -20,7 +21,7 @@ public class MultiServerPostBatchWorker extends MultiServerGenericPostWorker { +public class MultiServerReadBatchWorker extends MultiServerGenericReadWorker { public MultiServerReadBatchWorker(List clients, int minServers, BatchSpecificationMessage payload, int maxRetry, - FutureCallback futureCallback) { + FutureCallback futureCallback) { super(clients, minServers, payload, maxRetry, futureCallback); diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java index d331c53..49ceb7b 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java @@ -13,6 +13,7 @@ import static meerkat.bulletinboard.BulletinBoardConstants.*; import meerkat.comm.CommunicationException; import meerkat.comm.MessageOutputStream; +import meerkat.crypto.DigitalSignature; import meerkat.crypto.concrete.ECDSASignature; import meerkat.crypto.concrete.SHA256Digest; @@ -136,9 +137,9 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ new int[] {Types.BLOB, Types.INTEGER, Types.INTEGER, Types.BLOB} ), - CONNECT_BATCH_TAG( - new String[] {"SignerId", "BatchId", "Tag"}, - new int[] {Types.BLOB, Types.INTEGER, Types.VARCHAR} + STORE_BATCH_TAGS( + new String[] {"SignerId", "BatchId", "Tags"}, + new int[] {Types.BLOB, Types.INTEGER, Types.BLOB} ), GET_BATCH_TAGS( @@ -326,8 +327,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ protected NamedParameterJdbcTemplate jdbcTemplate; - protected BatchDigest digest; - protected BatchDigitalSignature signer; + protected BulletinBoardDigest digest; + protected DigitalSignature signer; protected List trusteeSignatureVerificationArray; protected int minTrusteeSignatures; @@ -363,7 +364,7 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ public void init(String meerkatDB) throws CommunicationException { // TODO write signature reading part. - digest = new GenericBatchDigest(new SHA256Digest()); + digest = new GenericBulletinBoardDigest(new SHA256Digest()); signer = new GenericBatchDigitalSignature(new ECDSASignature()); jdbcTemplate = new NamedParameterJdbcTemplate(sqlQueryProvider.getDataSource()); @@ -749,28 +750,15 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ return BoolMsg.newBuilder().setValue(false).build(); } - // Add new tags to table - ProtocolStringList tagList = message.getTagList(); - String[] tags = new String[tagList.size()]; - tags = tagList.toArray(tags); - try { - insertNewTags(tags); - } catch (SQLException e) { - throw new CommunicationException(e.getMessage()); - } + // Store tags + String sql = sqlQueryProvider.getSQLString(QueryType.STORE_BATCH_TAGS); + MapSqlParameterSource namedParameters = new MapSqlParameterSource(); - // Connect tags - String sql = sqlQueryProvider.getSQLString(QueryType.CONNECT_BATCH_TAG); - MapSqlParameterSource namedParameters[] = new MapSqlParameterSource[tags.length]; + namedParameters.addValue(QueryType.STORE_BATCH_TAGS.getParamName(0),message.getSignerId().toByteArray()); + namedParameters.addValue(QueryType.STORE_BATCH_TAGS.getParamName(1),message.getBatchId()); + namedParameters.addValue(QueryType.STORE_BATCH_TAGS.getParamName(2),message.toByteArray()); - for (int i=0 ; i < tags.length ; i++) { - namedParameters[i] = new MapSqlParameterSource(); - namedParameters[i].addValue(QueryType.CONNECT_BATCH_TAG.getParamName(0),message.getSignerId().toByteArray()); - namedParameters[i].addValue(QueryType.CONNECT_BATCH_TAG.getParamName(1),message.getBatchId()); - namedParameters[i].addValue(QueryType.CONNECT_BATCH_TAG.getParamName(2),tags[i]); - } - - jdbcTemplate.batchUpdate(sql,namedParameters); + jdbcTemplate.update(sql,namedParameters); return BoolMsg.newBuilder().setValue(true).build(); @@ -832,18 +820,18 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ namedParameters.addValue(QueryType.GET_BATCH_TAGS.getParamName(0),signerId.toByteArray()); namedParameters.addValue(QueryType.GET_BATCH_TAGS.getParamName(1),batchId); - List tags = jdbcTemplate.query(sql, namedParameters, new StringMapper()); + List beginBatchMessages = jdbcTemplate.query(sql, namedParameters, new BeginBatchMessageMapper()); - CompleteBatch completeBatch = new CompleteBatch( - BeginBatchMessage.newBuilder() - .setSignerId(signerId) - .setBatchId(batchId) - .addAllTag(tags) - .build() - ); + if (beginBatchMessages == null || beginBatchMessages.size() <= 0 || beginBatchMessages.get(0) == null) { + return BoolMsg.newBuilder().setValue(false).build(); + } - // Add timestamp to CompleteBatch - completeBatch.setTimestamp(message.getTimestamp()); + UnsignedBulletinBoardMessage unsignedMessage = UnsignedBulletinBoardMessage.newBuilder() + .addAllTag(beginBatchMessages.get(0).getTagList()) + .addTag(BATCH_TAG) + .addTag(batchIdToTag(batchId)) + .setTimestamp(message.getTimestamp()) + .build(); // Add actual batch data to CompleteBatch @@ -854,36 +842,14 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),batchId); namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2),0); // Read from the beginning - completeBatch.appendBatchData(jdbcTemplate.query(sql, namedParameters, new BatchDataMapper())); - - // Verify signature - - completeBatch.setSignature(message.getSig()); - -// try { -// TODO: Actual verification -// //signer.verify(completeBatch); -// } catch (CertificateException | InvalidKeyException | SignatureException e) { -// return BoolMsg.newBuilder().setValue(false).build(); -// } - - // Batch verified: finalize it - - // Calculate message ID - digest.reset(); - digest.update(completeBatch); - MessageID msgID = MessageID.newBuilder().setID(ByteString.copyFrom(digest.digest())).build(); + //TODO: Verification + // Will need to use the following to carry out the signature calculation: +// jdbcTemplate.query(sql, namedParameters, new BatchDataMapper()); // Create Bulletin Board message BulletinBoardMessage bulletinBoardMessage = BulletinBoardMessage.newBuilder() + .setMsg(unsignedMessage) .addSig(message.getSig()) - .setMsg(UnsignedBulletinBoardMessage.newBuilder() - .addAllTag(tags) - .addTag(BATCH_TAG) - .addTag(batchIdToTag(batchId)) - .setData(message.getSig().getSignerId()) - .setTimestamp(message.getTimestamp()) - .build()) .build(); // Post message without checking signature validity diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java index 55faa8f..8d97690 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java @@ -134,18 +134,16 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider QueryType.CHECK_BATCH_LENGTH.getParamName(0), QueryType.CHECK_BATCH_LENGTH.getParamName(1)); - case CONNECT_BATCH_TAG: + case STORE_BATCH_TAGS: return MessageFormat.format( - "INSERT INTO BatchTagTable (SignerId, BatchId, TagId) SELECT :{0}, :{1}, TagId FROM TagTable" - + " WHERE Tag = :{2}", - QueryType.CONNECT_BATCH_TAG.getParamName(0), - QueryType.CONNECT_BATCH_TAG.getParamName(1), - QueryType.CONNECT_BATCH_TAG.getParamName(2)); + "INSERT INTO BatchTagTable (SignerId, BatchId, TagId) VALUES (:{0}, :{1}, :{2})", + QueryType.STORE_BATCH_TAGS.getParamName(0), + QueryType.STORE_BATCH_TAGS.getParamName(1), + QueryType.STORE_BATCH_TAGS.getParamName(2)); case GET_BATCH_TAGS: return MessageFormat.format( - "SELECT Tag FROM TagTable INNER JOIN BatchTagTable ON TagTable.TagId = BatchTagTable.TagId" - + " WHERE SignerId = :{0} AND BatchId = :{1} ORDER BY Tag ASC", + "SELECT Tags FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}", QueryType.GET_BATCH_TAGS.getParamName(0), QueryType.GET_BATCH_TAGS.getParamName(1)); @@ -255,15 +253,14 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider + " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE)"); list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)"); - list.add("CREATE UNIQUE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId, EntryNum)"); + list.add("CREATE UNIQUE INDEX IF NOT EXISTS SignatureIndex ON SignatureTable(SignerId, EntryNum)"); list.add("CREATE TABLE IF NOT EXISTS BatchTable (SignerId TINYBLOB, BatchId INT, SerialNum INT, Data BLOB," + " UNIQUE(SignerId, BatchId, SerialNum))"); - list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, TagId INT," - + " FOREIGN KEY (TagId) REFERENCES TagTable(TagId) ON DELETE CASCADE)"); + list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, Tags BLOB)"); - list.add("CREATE INDEX IF NOT EXISTS BatchIndex ON BatchTagTable(SignerId, BatchId)"); + list.add("CREATE UNIQUE INDEX IF NOT EXISTS BatchIndex ON BatchTagTable(SignerId, BatchId)"); // This is used to create a simple table with one entry. // It is used for implementing a workaround for the missing INSERT IGNORE syntax diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BeginBatchMessageMapper.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BeginBatchMessageMapper.java new file mode 100644 index 0000000..0f72ec9 --- /dev/null +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BeginBatchMessageMapper.java @@ -0,0 +1,24 @@ +package meerkat.bulletinboard.sqlserver.mappers; + +import com.google.protobuf.InvalidProtocolBufferException; +import meerkat.protobuf.BulletinBoardAPI.BeginBatchMessage; +import org.springframework.jdbc.core.RowMapper; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Created by Arbel Deutsch Peled on 20-Dec-15. + */ +public class BeginBatchMessageMapper implements RowMapper { + + @Override + public BeginBatchMessage mapRow(ResultSet rs, int rowNum) throws SQLException { + try { + return BeginBatchMessage.newBuilder().mergeFrom(rs.getBytes(1)).build(); + } catch (InvalidProtocolBufferException e) { + return null; + } + } + +} diff --git a/bulletin-board-server/src/main/proto/meerkat/bulletin_board_server.proto b/bulletin-board-server/src/main/proto/meerkat/bulletin_board_server.proto deleted file mode 100644 index e31485b..0000000 --- a/bulletin-board-server/src/main/proto/meerkat/bulletin_board_server.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto3"; - -package meerkat; - -option java_package = "meerkat.protobuf"; - -message Boolean { - bool value = 1; -} \ No newline at end of file diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java index d74c1ea..aa23417 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java @@ -21,11 +21,23 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient { /** * Perform an end-to-end post of a signed batch message + * @param signerId is the canonical form for the ID of the sender of this batch + * @param batchId is a unique (per signer) ID for this batch * @param completeBatch contains all the data of the batch including the meta-data and the signature * @param callback is a class containing methods to handle the result of the operation * @return a unique identifier for the batch message */ - public MessageID postBatch(CompleteBatch completeBatch, FutureCallback callback); + public MessageID postBatch(byte[] signerId, int batchId, BulletinBoardMessage completeBatch, FutureCallback callback); + + /** + * Perform an end-to-end post of a signed batch message + * @param signerId is the canonical form for the ID of the sender of this batch + * @param batchId is a unique (per signer) ID for this batch + * @param completeBatch contains all the data of the batch including the meta-data and the signature + * @param callback is a class containing methods to handle the result of the operation + * @return a unique identifier for the batch message + */ + public MessageID postBatch(ByteString signerId, int batchId, BulletinBoardMessage completeBatch, FutureCallback callback); /** * This message informs the server about the existence of a new batch message and supplies it with the tags associated with it @@ -94,7 +106,7 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient { * @param batchSpecificationMessage contains the data required to specify a single batch instance * @param callback is a callback class for handling the result of the operation */ - public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback callback); + public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback callback); /** diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BatchDigest.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BatchDigest.java deleted file mode 100644 index 6e30fe9..0000000 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BatchDigest.java +++ /dev/null @@ -1,20 +0,0 @@ -package meerkat.bulletinboard; - -import meerkat.crypto.Digest; -import meerkat.protobuf.BulletinBoardAPI.*; - -import java.util.List; - -/** - * Created by Arbel Deutsch Peled on 18-Dec-15. - * Extends the Digest interface with a method for digesting Batch messages - */ -public interface BatchDigest extends Digest { - - /** - * Update the digest with the batch message data (ignore the signature) - * @param completeBatch is the batch message that needs to be digested - */ - public void update(CompleteBatch completeBatch); - -} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardDigest.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardDigest.java new file mode 100644 index 0000000..0521d73 --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardDigest.java @@ -0,0 +1,42 @@ +package meerkat.bulletinboard; + +import meerkat.crypto.Digest; +import meerkat.protobuf.BulletinBoardAPI.*; + +import java.util.List; + +/** + * Created by Arbel Deutsch Peled on 18-Dec-15. + * Extends the Digest interface with a method for digesting Batch messages + */ +public interface BulletinBoardDigest extends Digest { + + /** + * Update the digest with the batch message data (ignore the signature) + * The digest only uses the part the signatures are computed on for this operation + * @param completeBatch is the batch message that needs to be digested + */ + public void updateCompleteBatch(BulletinBoardMessage completeBatch); + + /** + * Update the digest with a BulletinBoardMessage that contains only the metadata of a batch message + * The digest only uses the part the signatures are computed on for this operation + * This operation is necessary before beginning to digest the actual data + * @param batchStub contains the metadata + */ + public void updateBatchStub(BulletinBoardMessage batchStub); + + /** + * Update the digest with the batch message data (ignore the signature) + * @param completeBatch is the batch message that needs to be digested + */ + public void updateCompleteBatch(UnsignedBulletinBoardMessage completeBatch); + + /** + * Update the digest with a BulletinBoardMessage that contains only the metadata of a batch message + * This operation is necessary before beginning to digest the actual data + * @param batchStub contains the metadata + */ + public void updateBatchStub(UnsignedBulletinBoardMessage batchStub); + +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBatchDigest.java b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBatchDigest.java deleted file mode 100644 index 852bb24..0000000 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBatchDigest.java +++ /dev/null @@ -1,61 +0,0 @@ -package meerkat.bulletinboard; - -import com.google.protobuf.Message; -import meerkat.crypto.Digest; -import meerkat.protobuf.BulletinBoardAPI.MessageID; -import meerkat.protobuf.BulletinBoardAPI.BatchData; - -import java.util.List; - - -/** - * Created by Arbel Deutsch Peled on 19-Dec-15. - * Wrapper class for digesting Batches in a standardized way - */ -public class GenericBatchDigest implements BatchDigest{ - - private Digest digest; - - public GenericBatchDigest(Digest digest) { - this.digest = digest; - } - - @Override - public void update(CompleteBatch completeBatch) { - - update(completeBatch.getBeginBatchMessage()); - - for (BatchData batchData : completeBatch.getBatchDataList()) { - update(batchData); - } - - update(completeBatch.getTimestamp()); - - } - - @Override - public byte[] digest() { - return digest.digest(); - } - - @Override - public MessageID digestAsMessageID() { - return digest.digestAsMessageID(); - } - - @Override - public void update(Message msg) { - digest.update(msg); - } - - @Override - public void reset() { - digest.reset(); - } - - @Override - public GenericBatchDigest clone() throws CloneNotSupportedException{ - return new GenericBatchDigest(digest.clone()); - } - -} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardDigest.java b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardDigest.java new file mode 100644 index 0000000..cc912a7 --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardDigest.java @@ -0,0 +1,79 @@ +package meerkat.bulletinboard; + +import com.google.protobuf.BytesValue; +import com.google.protobuf.Message; +import meerkat.crypto.Digest; +import meerkat.protobuf.BulletinBoardAPI; +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.BulletinBoardAPI.MessageID; + + +/** + * Created by Arbel Deutsch Peled on 19-Dec-15. + * Wrapper class for digesting Batches in a standardized way + */ +public class GenericBulletinBoardDigest implements BulletinBoardDigest { + + private Digest digest; + + public GenericBulletinBoardDigest(Digest digest) { + this.digest = digest; + } + + @Override + public byte[] digest() { + return digest.digest(); + } + + @Override + public MessageID digestAsMessageID() { + return digest.digestAsMessageID(); + } + + @Override + public void update(Message msg) { + digest.update(msg); + } + + @Override + public void update(byte[] data) { + digest.update(data); + } + + @Override + public void reset() { + digest.reset(); + } + + @Override + public GenericBulletinBoardDigest clone() throws CloneNotSupportedException{ + return new GenericBulletinBoardDigest(digest.clone()); + } + + @Override + public void updateCompleteBatch(BulletinBoardMessage completeBatch) { + updateCompleteBatch(completeBatch.getMsg()); + } + + @Override + public void updateBatchStub(BulletinBoardMessage batchStub) { + updateBatchStub(batchStub.getMsg()); + } + + @Override + public void updateCompleteBatch(UnsignedBulletinBoardMessage completeBatch) { + + // Digest just the signed part + UnsignedBulletinBoardMessage batchStub = completeBatch.toBuilder().clearData().build(); + updateBatchStub(batchStub); + + // Digest the data + update(completeBatch.getData().toByteArray()); + + } + + @Override + public void updateBatchStub(UnsignedBulletinBoardMessage batchStub) { + update(batchStub); + } +} diff --git a/meerkat-common/src/main/java/meerkat/crypto/Digest.java b/meerkat-common/src/main/java/meerkat/crypto/Digest.java index b7d86dc..e5202d6 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/Digest.java +++ b/meerkat-common/src/main/java/meerkat/crypto/Digest.java @@ -22,6 +22,12 @@ public interface Digest { */ public MessageID digestAsMessageID(); + /** + * Updates the digest using the given raw data + * @param data contains the raw data + */ + public void update (byte[] data); + /** * Updates the digest using the specified message (in serialized wire form) * diff --git a/meerkat-common/src/main/java/meerkat/crypto/concrete/SHA256Digest.java b/meerkat-common/src/main/java/meerkat/crypto/concrete/SHA256Digest.java index a7723ec..88f417c 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/concrete/SHA256Digest.java +++ b/meerkat-common/src/main/java/meerkat/crypto/concrete/SHA256Digest.java @@ -80,6 +80,7 @@ public class SHA256Digest implements Digest { hash.update(msg.asReadOnlyByteBuffer()); } + @Override final public void update(byte[] msg) { hash.update(msg); } From 1951db546dbb97e52f93a5d8e6c8d45acb3681fa Mon Sep 17 00:00:00 2001 From: Arbel Deutsch Peled Date: Sun, 19 Jun 2016 22:00:43 +0300 Subject: [PATCH 10/15] Changed Bulletin Board Message payload to either data or message ID Added server-generated unique batch identifiers Changed Client-side interfaces Refactored Client-side code for new batch mechanisms Not tested on client-side yet --- .../bulletinboard/BatchDataContainer.java | 7 +- .../CachedBulletinBoardClient.java | 245 ++++++++----- .../CachedClientBatchIdentifier.java | 29 ++ .../LocalBulletinBoardClient.java | 330 ++++++++++-------- .../MultiServerBatchIdentifier.java | 27 ++ .../bulletinboard/MultiServerWorker.java | 10 +- .../SimpleBulletinBoardClient.java | 187 ++++++---- .../SimpleBulletinBoardSynchronizer.java | 15 +- .../SingleServerBatchIdentifier.java | 42 +++ .../SingleServerBulletinBoardClient.java | 220 ++++++++---- .../ThreadedBulletinBoardClient.java | 78 +++-- .../MultiServerBeginBatchWorker.java | 80 ++++- .../MultiServerCloseBatchWorker.java | 22 +- .../MultiServerGenericPostWorker.java | 7 +- .../MultiServerGenericReadWorker.java | 11 +- .../MultiServerGetRedundancyWorker.java | 7 +- .../MultiServerPostBatchDataWorker.java | 50 ++- .../MultiServerPostBatchWorker.java | 10 +- .../MultiServerReadBatchDataWorker.java | 29 ++ .../MultiServerReadBatchWorker.java | 8 +- .../SingleServerBeginBatchWorker.java | 37 +- .../SingleServerReadBatchWorker.java | 6 +- .../sqlserver/BulletinBoardSQLServer.java | 128 +++---- .../sqlserver/H2QueryProvider.java | 92 ++--- .../sqlserver/MySQLQueryProvider.java | 91 ++--- .../sqlserver/SQLiteQueryProvider.java | 93 ++--- .../sqlserver/mappers/MessageStubMapper.java | 2 +- .../webapp/BulletinBoardWebApp.java | 3 +- .../GenericBulletinBoardServerTest.java | 59 +--- .../H2BulletinBoardServerTest.java | 10 - .../MySQLBulletinBoardServerTest.java | 10 - .../SQLiteBulletinBoardServerTest.java | 25 +- .../AsyncBulletinBoardClient.java | 70 ++-- .../bulletinboard/BulletinBoardClient.java | 20 +- .../bulletinboard/BulletinBoardServer.java | 9 +- .../GenericBulletinBoardDigest.java | 2 +- .../GenericBulletinBoardSignature.java | 2 +- .../util/BulletinBoardMessageComparator.java | 10 +- .../java/meerkat/util/BulletinBoardUtils.java | 44 +-- .../main/proto/meerkat/BulletinBoardAPI.proto | 31 +- 40 files changed, 1274 insertions(+), 884 deletions(-) create mode 100644 bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedClientBatchIdentifier.java create mode 100644 bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerBatchIdentifier.java create mode 100644 bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBatchIdentifier.java create mode 100644 bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/BatchDataContainer.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/BatchDataContainer.java index 884aad7..1026529 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/BatchDataContainer.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/BatchDataContainer.java @@ -1,6 +1,7 @@ package meerkat.bulletinboard; import meerkat.protobuf.BulletinBoardAPI.BatchChunk; +import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; import java.util.List; @@ -10,13 +11,11 @@ import java.util.List; */ public class BatchDataContainer { - public final byte[] signerId; - public final int batchId; + public final MultiServerBatchIdentifier batchId; public final List batchChunkList; public final int startPosition; - public BatchDataContainer(byte[] signerId, int batchId, List batchChunkList, int startPosition) { - this.signerId = signerId; + public BatchDataContainer(MultiServerBatchIdentifier batchId, List batchChunkList, int startPosition) { this.batchId = batchId; this.batchChunkList = batchChunkList; this.startPosition = startPosition; diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java index 3e1ed48..31521e1 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java @@ -1,11 +1,13 @@ package meerkat.bulletinboard; import com.google.common.util.concurrent.FutureCallback; -import com.google.protobuf.ByteString; +import com.google.protobuf.Timestamp; import meerkat.comm.CommunicationException; +import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Comm; +import meerkat.protobuf.Crypto.Signature; import meerkat.protobuf.Voting.*; -import meerkat.util.BulletinBoardUtils; import java.util.List; @@ -46,21 +48,12 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien for (BulletinBoardMessage msg : result) { try { - if (msg.getMsg().getTagList().contains(BulletinBoardConstants.BATCH_TAG)) { + if (msg.getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { // This is a batch message: need to upload batch data as well as the message itself - ByteString signerID = msg.getSig(0).getSignerId(); - int batchID = Integer.parseInt(BulletinBoardUtils.findTagWithPrefix(msg, BulletinBoardConstants.BATCH_ID_TAG_PREFIX)); + BulletinBoardMessage completeMessage = localClient.readBatchData(msg); - BatchSpecificationMessage batchSpecificationMessage = BatchSpecificationMessage.newBuilder() - .setSignerId(signerID) - .setBatchId(batchID) - .setStartPosition(0) - .build(); - - CompleteBatch completeBatch = localClient.readBatch(batchSpecificationMessage); - - localClient.postBatch(completeBatch); + localClient.postMessage(completeMessage); } else { @@ -126,12 +119,12 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien } @Override - public MessageID postBatch(final CompleteBatch completeBatch, final FutureCallback callback) { + public MessageID postAsBatch(final BulletinBoardMessage msg, final int chunkSize, final FutureCallback callback) { - return localClient.postBatch(completeBatch, new FutureCallback() { + return localClient.postAsBatch(msg, chunkSize, new FutureCallback() { @Override public void onSuccess(Boolean result) { - remoteClient.postBatch(completeBatch, callback); + remoteClient.postAsBatch(msg, chunkSize, callback); } @Override @@ -144,12 +137,31 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien } @Override - public void beginBatch(final BeginBatchMessage beginBatchMessage, final FutureCallback callback) { + public void beginBatch(final Iterable tags, final FutureCallback callback) { + + localClient.beginBatch(tags, new FutureCallback() { + + private BatchIdentifier localIdentifier; - localClient.beginBatch(beginBatchMessage, new FutureCallback() { @Override - public void onSuccess(Boolean result) { - remoteClient.beginBatch(beginBatchMessage, callback); + public void onSuccess(BatchIdentifier result) { + + localIdentifier = result; + + remoteClient.beginBatch(tags, new FutureCallback() { + @Override + public void onSuccess(BatchIdentifier result) { + if (callback != null) + callback.onSuccess(new CachedClientBatchIdentifier(localIdentifier, result)); + } + + @Override + public void onFailure(Throwable t) { + if (callback != null) + callback.onFailure(t); + } + }); + } @Override @@ -162,13 +174,19 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien } @Override - public void postBatchData(final byte[] signerId, final int batchId, final List batchChunkList, - final int startPosition, final FutureCallback callback) { + public void postBatchData(final BatchIdentifier batchIdentifier, final List batchChunkList, + final int startPosition, final FutureCallback callback) throws IllegalArgumentException{ - localClient.postBatchData(signerId, batchId, batchChunkList, startPosition, new FutureCallback() { + if (!(batchIdentifier instanceof CachedClientBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + final CachedClientBatchIdentifier identifier = (CachedClientBatchIdentifier) batchIdentifier; + + localClient.postBatchData(identifier.getLocalIdentifier(), batchChunkList, startPosition, new FutureCallback() { @Override public void onSuccess(Boolean result) { - remoteClient.postBatchData(signerId, batchId, batchChunkList, startPosition, callback); + remoteClient.postBatchData(identifier.getRemoteIdentifier(), batchChunkList, startPosition, callback); } @Override @@ -181,12 +199,19 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien } @Override - public void postBatchData(final byte[] signerId, final int batchId, final List batchChunkList, final FutureCallback callback) { + public void postBatchData(final BatchIdentifier batchIdentifier, final List batchChunkList, final FutureCallback callback) + throws IllegalArgumentException{ - localClient.postBatchData(signerId, batchId, batchChunkList, new FutureCallback() { + if (!(batchIdentifier instanceof CachedClientBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + final CachedClientBatchIdentifier identifier = (CachedClientBatchIdentifier) batchIdentifier; + + localClient.postBatchData(identifier.getLocalIdentifier(), batchChunkList, new FutureCallback() { @Override public void onSuccess(Boolean result) { - remoteClient.postBatchData(signerId, batchId, batchChunkList, callback); + remoteClient.postBatchData(identifier.getRemoteIdentifier(), batchChunkList, callback); } @Override @@ -199,49 +224,21 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien } @Override - public void postBatchData(final ByteString signerId, final int batchId, final List batchChunkList, - final int startPosition, final FutureCallback callback) { + public void closeBatch(final BatchIdentifier batchIdentifier, final Timestamp timestamp, final Iterable signatures, + final FutureCallback callback) { - localClient.postBatchData(signerId, batchId, batchChunkList, startPosition, new FutureCallback() { + if (!(batchIdentifier instanceof CachedClientBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + final CachedClientBatchIdentifier identifier = (CachedClientBatchIdentifier) batchIdentifier; + + localClient.closeBatch(identifier.getLocalIdentifier(), timestamp, signatures, new FutureCallback() { @Override public void onSuccess(Boolean result) { - remoteClient.postBatchData(signerId, batchId, batchChunkList, startPosition, callback); - } - @Override - public void onFailure(Throwable t) { - if (callback != null) - callback.onFailure(t); - } - }); + remoteClient.closeBatch(identifier.getRemoteIdentifier(), timestamp, signatures, callback); - } - - @Override - public void postBatchData(final ByteString signerId, final int batchId, final List batchChunkList, final FutureCallback callback) { - - localClient.postBatchData(signerId, batchId, batchChunkList, new FutureCallback() { - @Override - public void onSuccess(Boolean result) { - remoteClient.postBatchData(signerId, batchId, batchChunkList, callback); - } - - @Override - public void onFailure(Throwable t) { - if (callback != null) - callback.onFailure(t); - } - }); - - } - - @Override - public void closeBatch(final CloseBatchMessage closeBatchMessage, final FutureCallback callback) { - - localClient.closeBatch(closeBatchMessage, new FutureCallback() { - @Override - public void onSuccess(Boolean result) { - remoteClient.closeBatch(closeBatchMessage, callback); } @Override @@ -270,11 +267,12 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien } @Override - public void readBatch(final BatchSpecificationMessage batchSpecificationMessage, final FutureCallback callback) { + public void readBatch(final MessageID msgID, final FutureCallback callback) { + + localClient.readBatch(msgID, new FutureCallback() { - localClient.readBatch(batchSpecificationMessage, new FutureCallback() { @Override - public void onSuccess(CompleteBatch result) { + public void onSuccess(BulletinBoardMessage result) { if (callback != null) callback.onSuccess(result); // Read from local client was successful } @@ -284,19 +282,61 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien // Read from local unsuccessful: try to read from remote - remoteClient.readBatch(batchSpecificationMessage, new FutureCallback() { + remoteClient.readBatch(msgID, new FutureCallback() { @Override - public void onSuccess(CompleteBatch result) { + public void onSuccess(BulletinBoardMessage result) { // Read from remote was successful: store in local and return result - localClient.postBatch(result, new FutureCallback() { - @Override - public void onSuccess(Boolean result) {} - @Override - public void onFailure(Throwable t) {} - }); + localClient.postMessage(result, null); + + if (callback != null) + callback.onSuccess(result); + + } + + @Override + public void onFailure(Throwable t) { + + // Read from remote was unsuccessful: report error + if (callback != null) + callback.onFailure(t); + + } + + }); + + } + + }); + + } + + @Override + public void readBatchData(final BulletinBoardMessage stub, final FutureCallback callback) throws IllegalArgumentException { + + localClient.readBatchData(stub, new FutureCallback() { + + @Override + public void onSuccess(BulletinBoardMessage result) { + if (callback != null) + callback.onSuccess(result); // Read from local client was successful + } + + @Override + public void onFailure(Throwable t) { + + // Read from local unsuccessful: try to read from remote + + remoteClient.readBatchData(stub, new FutureCallback() { + + @Override + public void onSuccess(BulletinBoardMessage result) { + + // Read from remote was successful: store in local and return result + + localClient.postMessage(result, null); if (callback != null) callback.onSuccess(result); @@ -340,8 +380,10 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien } @Override - public MessageID postBatch(CompleteBatch completeBatch) throws CommunicationException { - return localClient.postBatch(completeBatch); + public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException { + MessageID result = localClient.postAsBatch(msg, chunkSize); + remoteClient.postAsBatch(msg, chunkSize); + return result; } @Override @@ -356,8 +398,49 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien } @Override - public CompleteBatch readBatch(BatchSpecificationMessage batchSpecificationMessage) throws CommunicationException { - return localClient.readBatch(batchSpecificationMessage); + public BulletinBoardMessage readBatch(MessageID msgID) throws CommunicationException { + + BulletinBoardMessage result = null; + try { + result = localClient.readBatch(msgID); + } catch (CommunicationException e) { + //TODO: log + } + + if (result == null){ + result = remoteClient.readBatch(msgID); + + if (result != null){ + localClient.postMessage(result); + } + + } + + return result; + + } + + @Override + public BulletinBoardMessage readBatchData(BulletinBoardMessage stub) throws CommunicationException, IllegalArgumentException { + + BulletinBoardMessage result = null; + try { + result = localClient.readBatchData(stub); + } catch (CommunicationException e) { + //TODO: log + } + + if (result == null){ + result = remoteClient.readBatchData(stub); + + if (result != null){ + localClient.postMessage(result); + } + + } + + return result; + } @Override diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedClientBatchIdentifier.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedClientBatchIdentifier.java new file mode 100644 index 0000000..322473d --- /dev/null +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedClientBatchIdentifier.java @@ -0,0 +1,29 @@ +package meerkat.bulletinboard; + +import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; + +import java.util.Arrays; + +/** + * Created by Arbel Deutsch Peled on 17-Jun-16. + */ +public final class CachedClientBatchIdentifier implements BatchIdentifier { + + // Per-server identifiers + private final BatchIdentifier localIdentifier; + private final BatchIdentifier remoteIdentifier; + + public CachedClientBatchIdentifier(BatchIdentifier localIdentifier, BatchIdentifier remoteIdentifier) { + this.localIdentifier = localIdentifier; + this.remoteIdentifier = remoteIdentifier; + } + + public BatchIdentifier getLocalIdentifier() { + return localIdentifier; + } + + public BatchIdentifier getRemoteIdentifier() { + return remoteIdentifier; + } + +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java index d982dd2..f09b2d3 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java @@ -1,20 +1,21 @@ package meerkat.bulletinboard; import com.google.common.util.concurrent.*; -import com.google.protobuf.ByteString; +import com.google.protobuf.Int64Value; +import com.google.protobuf.Timestamp; import meerkat.comm.CommunicationException; import meerkat.comm.MessageInputStream; import meerkat.comm.MessageInputStream.MessageInputStreamFactory; import meerkat.comm.MessageOutputStream; import meerkat.crypto.concrete.SHA256Digest; import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Crypto.Signature; import meerkat.protobuf.Voting.*; import meerkat.util.BulletinBoardUtils; import javax.ws.rs.NotFoundException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Executors; @@ -74,48 +75,57 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo private class CompleteBatchPoster implements Callable { - private final BulletinBoardMessage completeBatch; + private final BulletinBoardMessage msg; + private final int chunkSize; - public CompleteBatchPoster(BulletinBoardMessage completeBatch) { - this.completeBatch = completeBatch; + public CompleteBatchPoster(BulletinBoardMessage msg, int chunkSize) { + this.msg = msg; + this.chunkSize = chunkSize; } @Override public Boolean call() throws CommunicationException { - server.beginBatch(BeginBatchMessage.newBuilder().setSignerId(completeBatch.getSig(0)) - completeBatch.getBeginBatchMessage()); + BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder() + .addAllTag(msg.getMsg().getTagList()) + .build(); + + Int64Value batchId = server.beginBatch(beginBatchMessage); BatchMessage.Builder builder = BatchMessage.newBuilder() - .setSignerId(completeBatch.getSignature().getSignerId()) - .setBatchId(completeBatch.getBeginBatchMessage().getBatchId()); + .setBatchId(batchId.getValue()); + + List batchChunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize); int i=0; - for (BatchChunk data : completeBatch.getBatchDataList()){ + for (BatchChunk chunk : batchChunkList){ - server.postBatchMessage(builder.setSerialNum(i).setData(data).build()); + server.postBatchMessage(builder.setSerialNum(i).setData(chunk).build()); i++; } - return server.closeBatch(completeBatch.getCloseBatchMessage()).getValue(); + CloseBatchMessage closeBatchMessage = BulletinBoardUtils.generateCloseBatchMessage(batchId, batchChunkList.size(), msg); + + return server.closeBatch(closeBatchMessage).getValue(); } } @Override - public MessageID postBatch(ByteString signerId, int batchId, BulletinBoardMessage completeBatch, FutureCallback callback) { + public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize, FutureCallback callback) { - Futures.addCallback(executorService.submit(new CompleteBatchPoster(completeBatch)), callback); + Futures.addCallback(executorService.submit(new CompleteBatchPoster(msg, chunkSize)), callback); - digest.update(completeBatch); + digest.reset(); + digest.update(msg); return digest.digestAsMessageID(); } - private class BatchBeginner implements Callable { + private class BatchBeginner implements Callable { private final BeginBatchMessage msg; @@ -125,26 +135,29 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo @Override - public Boolean call() throws Exception { - return server.beginBatch(msg).getValue(); + public SingleServerBatchIdentifier call() throws Exception { + return new SingleServerBatchIdentifier(server.beginBatch(msg)); } } @Override - public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback callback) { + public void beginBatch(Iterable tags, FutureCallback callback) { + + BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder() + .addAllTag(tags) + .build(); + Futures.addCallback(executorService.submit(new BatchBeginner(beginBatchMessage)), callback); } private class BatchDataPoster implements Callable { - private final ByteString signerId; - private final int batchId; + private final SingleServerBatchIdentifier batchId; private final List batchChunkList; private final int startPosition; - public BatchDataPoster(ByteString signerId, int batchId, List batchChunkList, int startPosition) { - this.signerId = signerId; + public BatchDataPoster(SingleServerBatchIdentifier batchId, List batchChunkList, int startPosition) { this.batchId = batchId; this.batchChunkList = batchChunkList; this.startPosition = startPosition; @@ -155,8 +168,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo public Boolean call() throws Exception { BatchMessage.Builder msgBuilder = BatchMessage.newBuilder() - .setSignerId(signerId) - .setBatchId(batchId); + .setBatchId(batchId.getBatchId().getValue()); int i = startPosition; for (BatchChunk data : batchChunkList){ @@ -178,24 +190,28 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo } @Override - public void postBatchData(byte[] signerId, int batchId, List batchChunkList, int startPosition, FutureCallback callback) { - postBatchData(ByteString.copyFrom(signerId), batchId, batchChunkList, startPosition, callback); + public void postBatchData(BatchIdentifier batchId, List batchChunkList, int startPosition, FutureCallback callback) + throws IllegalArgumentException{ + + // Cast identifier to usable form + + if (!(batchId instanceof SingleServerBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + SingleServerBatchIdentifier identifier = (SingleServerBatchIdentifier) batchId; + + // Add worker + + Futures.addCallback(executorService.submit(new BatchDataPoster(identifier, batchChunkList, startPosition)), callback); + } @Override - public void postBatchData(byte[] signerId, int batchId, List batchChunkList, FutureCallback callback) { - postBatchData(signerId, batchId, batchChunkList, 0, callback); + public void postBatchData(BatchIdentifier batchId, List batchChunkList, FutureCallback callback) throws IllegalArgumentException{ + postBatchData(batchId, batchChunkList, 0, callback); } - @Override - public void postBatchData(ByteString signerId, int batchId, List batchChunkList, int startPosition, FutureCallback callback) { - Futures.addCallback(executorService.submit(new BatchDataPoster(signerId, batchId, batchChunkList, startPosition)), callback); - } - - @Override - public void postBatchData(ByteString signerId, int batchId, List batchChunkList, FutureCallback callback) { - postBatchData(signerId, batchId, batchChunkList, 0, callback); - } private class BatchCloser implements Callable { @@ -214,8 +230,26 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo } @Override - public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback callback) { + public void closeBatch(BatchIdentifier batchId, Timestamp timestamp, Iterable signatures, FutureCallback callback) { + + // Cast identifier to usable form + + if (!(batchId instanceof SingleServerBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + SingleServerBatchIdentifier identifier = (SingleServerBatchIdentifier) batchId; + + // Add worker + + CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder() + .setBatchId(identifier.getBatchId().getValue()) + .setTimestamp(timestamp) + .addAllSig(signatures) + .build(); + Futures.addCallback(executorService.submit(new BatchCloser(closeBatchMessage)), callback); + } private class RedundancyGetter implements Callable { @@ -287,31 +321,6 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo } - private class BatchDataReader implements Callable> { - - private final BatchSpecificationMessage batchSpecificationMessage; - - public BatchDataReader(BatchSpecificationMessage batchSpecificationMessage) { - this.batchSpecificationMessage = batchSpecificationMessage; - } - - @Override - public List call() throws Exception { - - ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); - MessageOutputStream outputStream = new MessageOutputStream<>(byteOutputStream); - server.readBatch(batchSpecificationMessage, outputStream); - - MessageInputStream inputStream = - MessageInputStreamFactory.createMessageInputStream( - new ByteArrayInputStream(byteOutputStream.toByteArray()), - BatchChunk.class); - - return inputStream.asList(); - - } - } - @Override public void readMessages(MessageFilterList filterList, FutureCallback> callback) { Futures.addCallback(executorService.submit(new MessageReader(filterList)), callback); @@ -387,83 +396,116 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo subscribe(filterList, 0, callback); } + private class BatchDataReader implements Callable> { + + private final MessageID msgID; + + public BatchDataReader(MessageID msgID) { + this.msgID = msgID; + } + + @Override + public List call() throws Exception { + + BatchQuery batchQuery = BatchQuery.newBuilder() + .setMsgID(msgID) + .setStartPosition(0) + .build(); + + ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); + MessageOutputStream batchOutputStream = new MessageOutputStream<>(byteOutputStream); + server.readBatch(batchQuery,batchOutputStream); + + MessageInputStream inputStream = + MessageInputStreamFactory.createMessageInputStream( + new ByteArrayInputStream(byteOutputStream.toByteArray()), + BatchChunk.class); + + return inputStream.asList(); + + } + } + private class CompleteBatchReader implements Callable { - private final BatchSpecificationMessage batchSpecificationMessage; + private final MessageID msgID; - public CompleteBatchReader(BatchSpecificationMessage batchSpecificationMessage) { - this.batchSpecificationMessage = batchSpecificationMessage; + public CompleteBatchReader(MessageID msgID) { + this.msgID = msgID; } @Override public BulletinBoardMessage call() throws Exception { - final String[] TAGS_TO_REMOVE = {BulletinBoardConstants.BATCH_TAG, BulletinBoardConstants.BATCH_ID_TAG_PREFIX}; - - CompleteBatch completeBatch = new CompleteBatch(BeginBatchMessage.newBuilder() - .setSignerId(batchSpecificationMessage.getSignerId()) - .setBatchId(batchSpecificationMessage.getBatchId()) - .build()); - - ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); - MessageOutputStream batchOutputStream = new MessageOutputStream<>(byteOutputStream); - server.readBatch(batchSpecificationMessage,batchOutputStream); - - MessageInputStream batchInputStream = - MessageInputStreamFactory.createMessageInputStream( - new ByteArrayInputStream(byteOutputStream.toByteArray()), - BatchChunk.class); - - completeBatch.appendBatchData(batchInputStream.asList()); + // Read message stub MessageFilterList filterList = MessageFilterList.newBuilder() .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(BulletinBoardConstants.BATCH_TAG) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(BulletinBoardConstants.BATCH_ID_TAG_PREFIX + completeBatch.getBeginBatchMessage().getBatchId()) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.SIGNER_ID) - .setId(completeBatch.getBeginBatchMessage().getSignerId()) + .setType(FilterType.MSG_ID) + .setId(msgID.getID()) .build()) .build(); - byteOutputStream = new ByteArrayOutputStream(); - MessageOutputStream messageOutputStream = new MessageOutputStream<>(byteOutputStream); - server.readMessages(filterList,messageOutputStream); + MessageReader messageReader = new MessageReader(filterList); + List bulletinBoardMessages = messageReader.call(); - MessageInputStream messageInputStream = - MessageInputStreamFactory.createMessageInputStream( - new ByteArrayInputStream(byteOutputStream.toByteArray()), - BulletinBoardMessage.class); - - if (!messageInputStream.isAvailable()) + if (bulletinBoardMessages.size() <= 0) { throw new NotFoundException("Batch does not exist"); + } - BulletinBoardMessage message = messageInputStream.readMessage(); + BulletinBoardMessage stub = bulletinBoardMessages.get(0); - completeBatch.setBeginBatchMessage(BeginBatchMessage.newBuilder() - .addAllTag(BulletinBoardUtils.removePrefixTags(message, Arrays.asList(TAGS_TO_REMOVE))) - .setSignerId(message.getSig(0).getSignerId()) - .setBatchId(Integer.parseInt(BulletinBoardUtils.findTagWithPrefix(message, BulletinBoardConstants.BATCH_ID_TAG_PREFIX))) - .build()); + // Read data - completeBatch.setSignature(message.getSig(0)); - completeBatch.setTimestamp(message.getMsg().getTimestamp()); + BatchDataReader batchDataReader = new BatchDataReader(msgID); + List batchChunkList = batchDataReader.call(); - return completeBatch; + // Combine and return + + return BulletinBoardUtils.gatherBatch(stub, batchChunkList); + + } + + } + + private class BatchDataCombiner implements Callable { + + private final BulletinBoardMessage stub; + + public BatchDataCombiner(BulletinBoardMessage stub) { + this.stub = stub; + } + + @Override + public BulletinBoardMessage call() throws Exception { + + MessageID msgID = MessageID.newBuilder().setID(stub.getMsg().getMsgId()).build(); + + BatchDataReader batchDataReader = new BatchDataReader(msgID); + + List batchChunkList = batchDataReader.call(); + + return BulletinBoardUtils.gatherBatch(stub, batchChunkList); } } @Override - public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback callback) { - Futures.addCallback(executorService.submit(new CompleteBatchReader(batchSpecificationMessage)), callback); + public void readBatch(MessageID msgID, FutureCallback callback) { + Futures.addCallback(executorService.submit(new CompleteBatchReader(msgID)), callback); + } + + @Override + public void readBatchData(BulletinBoardMessage stub, FutureCallback callback) throws IllegalArgumentException { + + if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID){ + throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); + } + + Futures.addCallback(executorService.submit(new BatchDataCombiner(stub)),callback); + } private class SyncQueryHandler implements Callable { @@ -506,12 +548,16 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo } @Override - public MessageID postBatch(CompleteBatch completeBatch) throws CommunicationException { + public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException { - CompleteBatchPoster poster = new CompleteBatchPoster(completeBatch); - poster.call(); + CompleteBatchPoster poster = new CompleteBatchPoster(msg, chunkSize); + Boolean result = poster.call(); - digest.update(completeBatch); + if (!result) + throw new CommunicationException("Batch post failed"); + + digest.reset(); + digest.update(msg); return digest.digestAsMessageID(); } @@ -545,38 +591,48 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo } @Override - public CompleteBatch readBatch(BatchSpecificationMessage batchSpecificationMessage) throws CommunicationException { + public BulletinBoardMessage readBatch(MessageID msgID) throws CommunicationException { MessageFilterList filterList = MessageFilterList.newBuilder() .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(BulletinBoardConstants.BATCH_TAG) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(BulletinBoardConstants.BATCH_ID_TAG_PREFIX + batchSpecificationMessage.getBatchId()) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.SIGNER_ID) - .setId(batchSpecificationMessage.getSignerId()) + .setType(FilterType.MSG_ID) + .setId(msgID.getID()) .build()) .build(); - BulletinBoardMessage batchMessage = readMessages(filterList).get(0); - - BatchDataReader batchDataReader = new BatchDataReader(batchSpecificationMessage); + CompleteBatchReader completeBatchReader = new CompleteBatchReader(msgID); try { - - List batchChunkList = batchDataReader.call(); - return new CompleteBatch(batchMessage, batchChunkList); - + return completeBatchReader.call(); } catch (Exception e) { - throw new CommunicationException("Error reading batch"); + throw new CommunicationException(e.getMessage() + " " + e.getMessage()); } } + @Override + public BulletinBoardMessage readBatchData(BulletinBoardMessage stub) throws CommunicationException, IllegalArgumentException { + + if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID){ + throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); + } + + MessageID msgID = MessageID.newBuilder().setID(stub.getMsg().getMsgId()).build(); + + BatchDataReader batchDataReader = new BatchDataReader(msgID); + + List batchChunkList = null; + + try { + batchChunkList = batchDataReader.call(); + } catch (Exception e) { + throw new CommunicationException(e.getCause() + " " + e.getMessage()); + } + + return BulletinBoardUtils.gatherBatch(stub, batchChunkList); + + } + @Override public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { return server.generateSyncQuery(generateSyncQueryParams); diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerBatchIdentifier.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerBatchIdentifier.java new file mode 100644 index 0000000..4934ee6 --- /dev/null +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerBatchIdentifier.java @@ -0,0 +1,27 @@ +package meerkat.bulletinboard; + +import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; + +import java.util.Arrays; + +/** + * Created by Arbel Deutsch Peled on 17-Jun-16. + */ +public final class MultiServerBatchIdentifier implements AsyncBulletinBoardClient.BatchIdentifier { + + // Per-server identifiers + private final Iterable identifiers; + + public MultiServerBatchIdentifier(Iterable identifiers) { + this.identifiers = identifiers; + } + + public MultiServerBatchIdentifier(BatchIdentifier[] identifiers) { + this.identifiers = Arrays.asList(identifiers); + } + + public Iterable getIdentifiers() { + return identifiers; + } + +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerWorker.java index 60327fa..0073be2 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerWorker.java @@ -18,7 +18,7 @@ import java.util.concurrent.atomic.AtomicInteger; */ public abstract class MultiServerWorker extends BulletinClientWorker implements Runnable, FutureCallback{ - private final List clients; + protected final List clients; protected AtomicInteger minServers; // The minimal number of servers the job must be successful on for the job to be completed @@ -91,14 +91,6 @@ public abstract class MultiServerWorker extends BulletinClientWorker getClientIterator() { - return clients.iterator(); - } - protected int getClientNumber() { return clients.size(); } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java index a93a0d2..6250784 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java @@ -2,12 +2,15 @@ package meerkat.bulletinboard; import com.google.protobuf.BoolValue; import com.google.protobuf.ByteString; +import com.google.protobuf.Int64Value; import meerkat.bulletinboard.workers.singleserver.*; import meerkat.comm.CommunicationException; import meerkat.crypto.concrete.SHA256Digest; +import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.protobuf.Voting.*; import meerkat.rest.*; +import meerkat.util.BulletinBoardUtils; import java.util.List; @@ -86,47 +89,6 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{ return MessageID.newBuilder().setID(ByteString.copyFrom(digest.digest())).build(); } - @Override - public MessageID postBatch(CompleteBatch completeBatch) throws CommunicationException { - - int pos = 0; - ByteString signerID = completeBatch.getSignature().getSignerId(); - int batchID = completeBatch.getBeginBatchMessage().getBatchId(); - - // Post message to all databases - - for (String db : meerkatDBs) { - - SingleServerBeginBatchWorker beginBatchWorker = new SingleServerBeginBatchWorker(db, completeBatch.getBeginBatchMessage(), 0); - - beginBatchWorker.call(); - - BatchMessage.Builder builder = BatchMessage.newBuilder().setSignerId(signerID).setBatchId(batchID); - - for (BatchChunk batchChunk : completeBatch.getBatchDataList()) { - - SingleServerPostBatchWorker postBatchWorker = - new SingleServerPostBatchWorker( - db, - builder.setData(batchChunk).setSerialNum(pos).build(), - 0); - - postBatchWorker.call(); - - pos++; - - } - - SingleServerCloseBatchWorker closeBatchWorker = new SingleServerCloseBatchWorker(db, completeBatch.getCloseBatchMessage(), 0); - - closeBatchWorker.call(); - - } - - digest.update(completeBatch); - return digest.digestAsMessageID(); - } - /** * Access each database and search for a given message ID * Return the number of databases in which the message was found @@ -173,17 +135,18 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{ * @return the list of Bulletin Board messages that are returned from a server */ @Override - public List readMessages(MessageFilterList filterList) { + public List readMessages(MessageFilterList filterList) throws CommunicationException{ // Replace null filter list with blank one. if (filterList == null){ filterList = MessageFilterList.getDefaultInstance(); } + String exceptionString = ""; + for (String db : meerkatDBs) { try { - webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH); SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(db, filterList, 0); @@ -191,33 +154,92 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{ return result; - } catch (Exception ignored) {} + } catch (Exception e) { + //TODO: log + exceptionString += e.getMessage() + "\n"; + } } - return null; + throw new CommunicationException("Could not find message in any DB. Errors follow:\n" + exceptionString); } @Override - public CompleteBatch readBatch(BatchSpecificationMessage batchSpecificationMessage) throws CommunicationException { + public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException { - // Create job with no retries for retrieval of the Bulletin Board Message that defines the batch + List chunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize); + + BeginBatchMessage beginBatchMessage = BulletinBoardUtils.generateBeginBatchMessage(msg); + + boolean posted = false; + + // Post message to all databases + + for (String db : meerkatDBs) { + + try { + + int pos = 0; + + SingleServerBeginBatchWorker beginBatchWorker = new SingleServerBeginBatchWorker(db, beginBatchMessage, 0); + + Int64Value batchId = beginBatchWorker.call(); + + BatchMessage.Builder builder = BatchMessage.newBuilder().setBatchId(batchId.getValue()); + + for (BatchChunk batchChunk : chunkList) { + + SingleServerPostBatchWorker postBatchWorker = + new SingleServerPostBatchWorker( + db, + builder.setData(batchChunk).setSerialNum(pos).build(), + 0); + + postBatchWorker.call(); + + pos++; + + } + + CloseBatchMessage closeBatchMessage = BulletinBoardUtils.generateCloseBatchMessage(batchId, chunkList.size(), msg); + + SingleServerCloseBatchWorker closeBatchWorker = new SingleServerCloseBatchWorker(db, closeBatchMessage, 0); + + closeBatchWorker.call(); + + posted = true; + + } catch(Exception ignored) {} + + } + + if (!posted){ + throw new CommunicationException("Could not post to any server"); + } + + digest.reset(); + digest.update(msg); + return digest.digestAsMessageID(); + + } + + @Override + public BulletinBoardMessage readBatch(MessageID msgID) throws CommunicationException { MessageFilterList filterList = MessageFilterList.newBuilder() .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(BulletinBoardConstants.BATCH_TAG) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(BulletinBoardConstants.BATCH_ID_TAG_PREFIX + batchSpecificationMessage.getBatchId()) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.SIGNER_ID) - .setId(batchSpecificationMessage.getSignerId()) + .setType(FilterType.MSG_ID) + .setId(msgID.getID()) .build()) .build(); + BatchQuery batchQuery = BatchQuery.newBuilder() + .setMsgID(msgID) + .setStartPosition(0) + .build(); + + String exceptionString = ""; + for (String db : meerkatDBs) { try { @@ -228,20 +250,57 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{ if (messages == null || messages.size() < 1) continue; - BulletinBoardMessage batchMessage = messages.get(0); + BulletinBoardMessage stub = messages.get(0); - SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(db, batchSpecificationMessage, 0); + SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(db, batchQuery, 0); List batchChunkList = batchWorker.call(); - CompleteBatch result = new CompleteBatch(batchMessage, batchChunkList); + return BulletinBoardUtils.gatherBatch(stub, batchChunkList); - return result; - - } catch (Exception ignored) {} + } catch (Exception e) { + //TODO: log + exceptionString += e.getMessage() + "\n"; + } } - return null; + throw new CommunicationException("Could not find message in any DB. Errors follow:\n" + exceptionString); + + } + + @Override + public BulletinBoardMessage readBatchData(BulletinBoardMessage stub) throws CommunicationException, IllegalArgumentException { + + if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID){ + throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); + } + + BatchQuery batchQuery = BatchQuery.newBuilder() + .setMsgID(MessageID.newBuilder() + .setID(stub.getMsg().getMsgId()) + .build()) + .setStartPosition(0) + .build(); + + String exceptionString = ""; + + for (String db : meerkatDBs) { + + try { + + SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(db, batchQuery, 0); + + List batchChunkList = batchWorker.call(); + + return BulletinBoardUtils.gatherBatch(stub, batchChunkList); + + } catch (Exception e) { + //TODO: log + exceptionString += e.getMessage() + "\n"; + } + } + + throw new CommunicationException("Could not find message in any DB. Errors follow:\n" + exceptionString); } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java index 7700fd6..e8abf6b 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java @@ -137,22 +137,13 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize try { - if (message.getMsg().getTagList().contains(BulletinBoardConstants.BATCH_TAG)) { + if (message.getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { // This is a batch message: need to upload batch data as well as the message itself - ByteString signerID = message.getSig(0).getSignerId(); - int batchID = Integer.parseInt(BulletinBoardUtils.findTagWithPrefix(message, BulletinBoardConstants.BATCH_ID_TAG_PREFIX)); - BatchSpecificationMessage batchSpecificationMessage = BatchSpecificationMessage.newBuilder() - .setSignerId(signerID) - .setBatchId(batchID) - .setStartPosition(0) - .build(); + BulletinBoardMessage completeMsg = localClient.readBatchData(message); - - CompleteBatch completeBatch = localClient.readBatch(batchSpecificationMessage); - - remoteClient.postBatch(completeBatch, new MessageDeleteCallback(message.getEntryNum(), syncStatusUpdateCallback)); + remoteClient.postMessage(completeMsg, new MessageDeleteCallback(message.getEntryNum(), syncStatusUpdateCallback)); } else { diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBatchIdentifier.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBatchIdentifier.java new file mode 100644 index 0000000..9c4ad40 --- /dev/null +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBatchIdentifier.java @@ -0,0 +1,42 @@ +package meerkat.bulletinboard; + +import com.google.protobuf.Int64Value; + +/** + * Created by Arbel Deutsch Peled on 16-Jun-16. + * Single-server implementation of the BatchIdentifier interface + */ +final class SingleServerBatchIdentifier implements AsyncBulletinBoardClient.BatchIdentifier { + + private final Int64Value batchId; + + private int length; + + public SingleServerBatchIdentifier(Int64Value batchId) { + this.batchId = batchId; + length = 0; + } + + public SingleServerBatchIdentifier(long batchId) { + this(Int64Value.newBuilder().setValue(batchId).build()); + } + + public Int64Value getBatchId() { + return batchId; + } + + /** + * Overrides the existing length with the new one only if the new length is longer + * @param newLength + */ + public void setLength(int newLength) { + if (newLength > length) { + length = newLength; + } + } + + public int getLength() { + return length; + } + +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java index 6ef013f..b985a8a 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java @@ -4,11 +4,15 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListeningScheduledExecutorService; import com.google.common.util.concurrent.MoreExecutors; -import com.google.protobuf.ByteString; +import com.google.protobuf.Int64Value; +import com.google.protobuf.Timestamp; import meerkat.bulletinboard.workers.singleserver.*; import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Crypto; import meerkat.protobuf.Voting.BulletinBoardClientParams; +import meerkat.util.BulletinBoardUtils; +import java.lang.Iterable; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -124,14 +128,14 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i * It reports success back to the user only if all of the batch-data were successfully posted * If any batch-data fails to post: this callback reports failure */ - class PostBatchDataListCallback implements FutureCallback { + class PostBatchChunkListCallback implements FutureCallback { private final FutureCallback callback; private AtomicInteger batchDataRemaining; private AtomicBoolean aggregatedResult; - public PostBatchDataListCallback(int batchDataLength, FutureCallback callback) { + public PostBatchChunkListCallback(int batchDataLength, FutureCallback callback) { this.callback = callback; this.batchDataRemaining = new AtomicInteger(batchDataLength); @@ -169,15 +173,15 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i */ class CompleteBatchReadCallback { - private final FutureCallback callback; + private final FutureCallback callback; private List batchChunkList; - private BulletinBoardMessage batchMessage; + private BulletinBoardMessage stub; private AtomicInteger remainingQueries; private AtomicBoolean failed; - public CompleteBatchReadCallback(FutureCallback callback) { + public CompleteBatchReadCallback(FutureCallback callback) { this.callback = callback; @@ -188,14 +192,10 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i protected void combineAndReturn() { - final String[] prefixes = { - BulletinBoardConstants.BATCH_ID_TAG_PREFIX, - BulletinBoardConstants.BATCH_TAG}; - if (remainingQueries.decrementAndGet() == 0){ if (callback != null) - callback.onSuccess(new CompleteBatch(batchMessage, batchChunkList)); + callback.onSuccess(BulletinBoardUtils.gatherBatch(stub, batchChunkList)); } } @@ -241,7 +241,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i return; } - batchMessage = result.get(0); + stub = result.get(0); combineAndReturn(); } @@ -367,18 +367,22 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i private class PostBatchDataCallback implements FutureCallback { - private final CompleteBatch completeBatch; + private final BulletinBoardMessage msg; + private final BatchIdentifier identifier; private final FutureCallback callback; - public PostBatchDataCallback(CompleteBatch completeBatch, FutureCallback callback) { - this.completeBatch = completeBatch; + public PostBatchDataCallback(BulletinBoardMessage msg, BatchIdentifier identifier, FutureCallback callback) { + this.msg = msg; + this.identifier = identifier; this.callback = callback; } @Override - public void onSuccess(Boolean msg) { + public void onSuccess(Boolean result) { closeBatch( - completeBatch.getCloseBatchMessage(), + identifier, + msg.getMsg().getTimestamp(), + msg.getSigList(), callback ); } @@ -391,25 +395,28 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i } - private class BeginBatchCallback implements FutureCallback { + private class ContinueBatchCallback implements FutureCallback { - private final CompleteBatch completeBatch; + private final BulletinBoardMessage msg; + private final int chunkSize; private final FutureCallback callback; - public BeginBatchCallback(CompleteBatch completeBatch, FutureCallback callback) { - this.completeBatch = completeBatch; + public ContinueBatchCallback(BulletinBoardMessage msg, int chunkSize, FutureCallback callback) { + this.msg = msg; + this.chunkSize = chunkSize; this.callback = callback; } @Override - public void onSuccess(Boolean msg) { + public void onSuccess(BatchIdentifier identifier) { + + List batchChunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize); postBatchData( - completeBatch.getBeginBatchMessage().getSignerId(), - completeBatch.getBeginBatchMessage().getBatchId(), - completeBatch.getBatchDataList(), + identifier, + batchChunkList, 0, - new PostBatchDataCallback(completeBatch,callback)); + new PostBatchDataCallback(msg, identifier, callback)); } @Override @@ -420,45 +427,80 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i } @Override - public MessageID postBatch(CompleteBatch completeBatch, FutureCallback callback) { + public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize, FutureCallback callback) { beginBatch( - completeBatch.getBeginBatchMessage(), - new BeginBatchCallback(completeBatch, callback) + msg.getMsg().getTagList(), + new ContinueBatchCallback(msg, chunkSize, callback) ); - digest.update(completeBatch); + digest.update(msg); return digest.digestAsMessageID(); } + private class BeginBatchCallback implements FutureCallback { + + private final FutureCallback callback; + + public BeginBatchCallback(FutureCallback callback) { + this.callback = callback; + } + + @Override + public void onSuccess(Int64Value result) { + callback.onSuccess(new SingleServerBatchIdentifier(result)); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + } + @Override - public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback callback) { + public void beginBatch(Iterable tags, FutureCallback callback) { + + BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder() + .addAllTag(tags) + .build(); // Create worker with redundancy 1 and MAX_RETRIES retries SingleServerBeginBatchWorker worker = new SingleServerBeginBatchWorker(meerkatDBs.get(0), beginBatchMessage, MAX_RETRIES); // Submit worker and create callback - scheduleWorker(worker, new RetryCallback<>(worker, callback)); + scheduleWorker(worker, new RetryCallback<>(worker, new BeginBatchCallback(callback))); } - @Override - public void postBatchData(ByteString signerId, int batchId, List batchChunkList, - int startPosition, FutureCallback callback) { - BatchMessage.Builder builder = BatchMessage.newBuilder() - .setSignerId(signerId) - .setBatchId(batchId); + @Override + public void postBatchData(BatchIdentifier batchIdentifier, List batchChunkList, + int startPosition, FutureCallback callback) throws IllegalArgumentException{ + + // Cast identifier to usable form + + if (!(batchIdentifier instanceof SingleServerBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + SingleServerBatchIdentifier identifier = (SingleServerBatchIdentifier) batchIdentifier; + + // Update batch size + + identifier.setLength(startPosition + batchChunkList.size()); // Create a unified callback to aggregate successful posts - PostBatchDataListCallback listCallback = new PostBatchDataListCallback(batchChunkList.size(), callback); + PostBatchChunkListCallback listCallback = new PostBatchChunkListCallback(batchChunkList.size(), callback); // Iterate through data list + BatchMessage.Builder builder = BatchMessage.newBuilder() + .setBatchId(identifier.getBatchId().getValue()); + for (BatchChunk data : batchChunkList) { builder.setSerialNum(startPosition).setData(data); @@ -476,29 +518,29 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i } @Override - public void postBatchData(ByteString signerId, int batchId, List batchChunkList, FutureCallback callback) { + public void postBatchData(BatchIdentifier batchIdentifier, List batchChunkList, FutureCallback callback) + throws IllegalArgumentException { - postBatchData(signerId, batchId, batchChunkList, 0, callback); + postBatchData(batchIdentifier, batchChunkList, 0, callback); } @Override - public void postBatchData(byte[] signerId, int batchId, List batchChunkList, - int startPosition, FutureCallback callback) { + public void closeBatch(BatchIdentifier batchIdentifier, Timestamp timestamp, Iterable signatures, FutureCallback callback) + throws IllegalArgumentException { - postBatchData(ByteString.copyFrom(signerId), batchId, batchChunkList, startPosition, callback); + if (!(batchIdentifier instanceof SingleServerBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } - } + SingleServerBatchIdentifier identifier = (SingleServerBatchIdentifier) batchIdentifier; - @Override - public void postBatchData(byte[] signerId, int batchId, List batchChunkList, FutureCallback callback) { - - postBatchData(signerId, batchId, batchChunkList, 0, callback); - - } - - @Override - public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback callback) { + CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder() + .setBatchId(identifier.getBatchId().getValue()) + .setBatchLength(identifier.getLength()) + .setTimestamp(timestamp) + .addAllSig(signatures) + .build(); // Create worker with redundancy 1 and MAX_RETRIES retries SingleServerCloseBatchWorker worker = @@ -532,29 +574,26 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i } @Override - public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback callback) { + public void readBatch(MessageID msgID, FutureCallback callback) { - // Create job with no retries for retrieval of the Bulletin Board Message that defines the batch + // Create job with MAX retries for retrieval of the Bulletin Board Message that defines the batch MessageFilterList filterList = MessageFilterList.newBuilder() .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(BulletinBoardConstants.BATCH_TAG) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(BulletinBoardConstants.BATCH_ID_TAG_PREFIX + batchSpecificationMessage.getBatchId()) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.SIGNER_ID) - .setId(batchSpecificationMessage.getSignerId()) + .setType(FilterType.MSG_ID) + .setId(msgID.getID()) .build()) .build(); - SingleServerReadMessagesWorker messageWorker = new SingleServerReadMessagesWorker(meerkatDBs.get(0), filterList, 1); + BatchQuery batchQuery = BatchQuery.newBuilder() + .setMsgID(msgID) + .setStartPosition(0) + .build(); - // Create job with no retries for retrieval of the Batch Data List - SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(meerkatDBs.get(0), batchSpecificationMessage, 1); + SingleServerReadMessagesWorker messageWorker = new SingleServerReadMessagesWorker(meerkatDBs.get(0), filterList, MAX_RETRIES); + + // Create job with MAX retries for retrieval of the Batch Data List + SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(meerkatDBs.get(0), batchQuery, MAX_RETRIES); // Create callback that will combine the two worker products CompleteBatchReadCallback completeBatchReadCallback = new CompleteBatchReadCallback(callback); @@ -565,6 +604,49 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i } + private class ReadBatchCallback implements FutureCallback> { + + private final BulletinBoardMessage stub; + private final FutureCallback callback; + + public ReadBatchCallback(BulletinBoardMessage stub, FutureCallback callback) { + this.stub = stub; + this.callback = callback; + } + + @Override + public void onSuccess(List result) { + callback.onSuccess(BulletinBoardUtils.gatherBatch(stub, result)); + } + + @Override + public void onFailure(Throwable t) { + + } + } + + @Override + public void readBatchData(BulletinBoardMessage stub, FutureCallback callback) throws IllegalArgumentException{ + + if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { + throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); + } + + // Create job with MAX retries for retrieval of the Batch Data List + + BatchQuery batchQuery = BatchQuery.newBuilder() + .setMsgID(MessageID.newBuilder() + .setID(stub.getMsg().getMsgId()) + .build()) + .setStartPosition(0) + .build(); + + SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(meerkatDBs.get(0), batchQuery, MAX_RETRIES); + + scheduleWorker(batchWorker, new RetryCallback<>(batchWorker, new ReadBatchCallback(stub, callback))); + + } + @Override public void querySync(SyncQuery syncQuery, FutureCallback callback) { diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java index 289ffcf..7f8232f 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java @@ -1,10 +1,12 @@ package meerkat.bulletinboard; import com.google.common.util.concurrent.FutureCallback; -import com.google.protobuf.ByteString; +import com.google.protobuf.Timestamp; import meerkat.bulletinboard.workers.multiserver.*; +import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Crypto.Signature; import meerkat.protobuf.Voting.*; import java.util.ArrayList; @@ -41,6 +43,7 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple private int minAbsoluteRedundancy; + /** * Stores database locations and initializes the web Client * Stores the required minimum redundancy. @@ -98,28 +101,28 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple } @Override - public MessageID postBatch(BulletinBoardMessage completeBatch, FutureCallback callback) { + public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize, FutureCallback callback) { // Create job MultiServerPostBatchWorker worker = - new MultiServerPostBatchWorker(clients, minAbsoluteRedundancy, completeBatch, POST_MESSAGE_RETRY_NUM, callback); + new MultiServerPostBatchWorker(clients, minAbsoluteRedundancy, msg, chunkSize, POST_MESSAGE_RETRY_NUM, callback); // Submit job executorService.submit(worker); // Calculate the correct message ID and return it batchDigest.reset(); - batchDigest.update(completeBatch); + batchDigest.update(msg); return batchDigest.digestAsMessageID(); } @Override - public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback callback) { + public void beginBatch(Iterable tags, FutureCallback callback) { // Create job MultiServerBeginBatchWorker worker = - new MultiServerBeginBatchWorker(clients, minAbsoluteRedundancy, beginBatchMessage, POST_MESSAGE_RETRY_NUM, callback); + new MultiServerBeginBatchWorker(clients, minAbsoluteRedundancy, tags, POST_MESSAGE_RETRY_NUM, callback); // Submit job executorService.submit(worker); @@ -127,10 +130,18 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple } @Override - public void postBatchData(byte[] signerId, int batchId, List batchChunkList, - int startPosition, FutureCallback callback) { + public void postBatchData(BatchIdentifier batchIdentifier, List batchChunkList, + int startPosition, FutureCallback callback) throws IllegalArgumentException { - BatchDataContainer batchDataContainer = new BatchDataContainer(signerId, batchId, batchChunkList, startPosition); + // Cast identifier to usable form + + if (!(batchIdentifier instanceof MultiServerBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + MultiServerBatchIdentifier identifier = (MultiServerBatchIdentifier) batchIdentifier; + + BatchDataContainer batchDataContainer = new BatchDataContainer(identifier, batchChunkList, startPosition); // Create job MultiServerPostBatchDataWorker worker = @@ -142,33 +153,19 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple } @Override - public void postBatchData(byte[] signerId, int batchId, List batchChunkList, FutureCallback callback) { + public void postBatchData(BatchIdentifier batchIdentifier, List batchChunkList, FutureCallback callback) + throws IllegalArgumentException { - postBatchData(signerId, batchId, batchChunkList, 0, callback); + postBatchData(batchIdentifier, batchChunkList, 0, callback); } @Override - public void postBatchData(ByteString signerId, int batchId, List batchChunkList, - int startPosition, FutureCallback callback) { - - postBatchData(signerId.toByteArray(), batchId, batchChunkList, startPosition, callback); - - } - - @Override - public void postBatchData(ByteString signerId, int batchId, List batchChunkList, FutureCallback callback) { - - postBatchData(signerId, batchId, batchChunkList, 0, callback); - - } - - @Override - public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback callback) { + public void closeBatch(BatchIdentifier payload, Timestamp timestamp, Iterable signatures, FutureCallback callback) { // Create job MultiServerCloseBatchWorker worker = - new MultiServerCloseBatchWorker(clients, minAbsoluteRedundancy, closeBatchMessage, POST_MESSAGE_RETRY_NUM, callback); + new MultiServerCloseBatchWorker(clients, minAbsoluteRedundancy, payload, timestamp, signatures, POST_MESSAGE_RETRY_NUM, callback); // Submit job executorService.submit(worker); @@ -211,11 +208,29 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple } @Override - public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback callback) { + public void readBatch(MessageID msgID, FutureCallback callback) { + + //Create job + MultiServerReadBatchWorker worker = + new MultiServerReadBatchWorker(clients, minAbsoluteRedundancy, msgID, READ_MESSAGES_RETRY_NUM, callback); + + // Submit job + executorService.submit(worker); + + } + + @Override + public void readBatchData(BulletinBoardMessage stub, FutureCallback callback) throws IllegalArgumentException { + + if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { + throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); + } + + MessageID msgID = MessageID.newBuilder().setID(stub.getMsg().getMsgId()).build(); // Create job - MultiServerReadBatchWorker worker = - new MultiServerReadBatchWorker(clients, minAbsoluteRedundancy, batchSpecificationMessage, READ_MESSAGES_RETRY_NUM, callback); + MultiServerReadBatchDataWorker worker = + new MultiServerReadBatchDataWorker(clients, minAbsoluteRedundancy, msgID, READ_MESSAGES_RETRY_NUM, callback); // Submit job executorService.submit(worker); @@ -251,4 +266,3 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple } } -==== BASE ==== diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerBeginBatchWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerBeginBatchWorker.java index e0e92bb..793fc6e 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerBeginBatchWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerBeginBatchWorker.java @@ -1,28 +1,96 @@ package meerkat.bulletinboard.workers.multiserver; import com.google.common.util.concurrent.FutureCallback; +import meerkat.bulletinboard.MultiServerBatchIdentifier; +import meerkat.bulletinboard.MultiServerWorker; import meerkat.bulletinboard.SingleServerBulletinBoardClient; -import meerkat.protobuf.BulletinBoardAPI.BeginBatchMessage; +import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; +import meerkat.comm.CommunicationException; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; /** * Created by Arbel Deutsch Peled on 27-Dec-15. */ -public class MultiServerBeginBatchWorker extends MultiServerGenericPostWorker { +public class MultiServerBeginBatchWorker extends MultiServerWorker, BatchIdentifier> { + + private BatchIdentifier[] identifiers; + private AtomicInteger remainingServers; public MultiServerBeginBatchWorker(List clients, - int minServers, BeginBatchMessage payload, int maxRetry, - FutureCallback futureCallback) { + int minServers, Iterable payload, int maxRetry, + FutureCallback futureCallback) { super(clients, minServers, payload, maxRetry, futureCallback); + identifiers = new BatchIdentifier[clients.size()]; + + for (int i = 0 ; i < identifiers.length ; i++) { + identifiers[i] = null; + } + + remainingServers = new AtomicInteger(clients.size()); + + } + + private class BeginBatchCallback implements FutureCallback { + + private final int clientNum; + + public BeginBatchCallback(int clientNum) { + this.clientNum = clientNum; + } + + private void finishPost() { + + if (remainingServers.decrementAndGet() <= 0){ + + if (minServers.get() <= 0) { + MultiServerBeginBatchWorker.this.onSuccess(new MultiServerBatchIdentifier(identifiers)); + } else { + MultiServerBeginBatchWorker.this.onFailure(new CommunicationException("Could not open batch in enough servers")); + } + } + + } + + @Override + public void onSuccess(BatchIdentifier result) { + + identifiers[clientNum] = result; + finishPost(); + + } + + @Override + public void onFailure(Throwable t) { + finishPost(); + } } @Override - protected void doPost(SingleServerBulletinBoardClient client, BeginBatchMessage payload) { - client.beginBatch(payload, this); + public void onSuccess(BatchIdentifier result) { + succeed(result); } + @Override + public void onFailure(Throwable t) { + fail(t); + } + @Override + public void run() { + + int clientNum = 0; + + for (SingleServerBulletinBoardClient client : clients){ + + client.beginBatch(payload, new BeginBatchCallback(clientNum)); + + clientNum++; + + } + + } } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerCloseBatchWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerCloseBatchWorker.java index 300440f..68aa8ef 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerCloseBatchWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerCloseBatchWorker.java @@ -1,27 +1,35 @@ package meerkat.bulletinboard.workers.multiserver; import com.google.common.util.concurrent.FutureCallback; +import com.google.protobuf.Timestamp; +import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; import meerkat.bulletinboard.SingleServerBulletinBoardClient; -import meerkat.protobuf.BulletinBoardAPI.CloseBatchMessage; +import meerkat.protobuf.Crypto.Signature; import java.util.List; /** * Created by Arbel Deutsch Peled on 27-Dec-15. */ -public class MultiServerCloseBatchWorker extends MultiServerGenericPostWorker { +public class MultiServerCloseBatchWorker extends MultiServerGenericPostWorker { - public MultiServerCloseBatchWorker(List clients, - int minServers, CloseBatchMessage payload, int maxRetry, - FutureCallback futureCallback) { + private final Timestamp timestamp; + private final Iterable signatures; + + public MultiServerCloseBatchWorker(List clients, int minServers, + BatchIdentifier payload, Timestamp timestamp, Iterable signatures, + int maxRetry, FutureCallback futureCallback) { super(clients, minServers, payload, maxRetry, futureCallback); + this.timestamp = timestamp; + this.signatures = signatures; + } @Override - protected void doPost(SingleServerBulletinBoardClient client, CloseBatchMessage payload) { - client.closeBatch(payload, this); + protected void doPost(SingleServerBulletinBoardClient client, BatchIdentifier payload) { + client.closeBatch(payload, timestamp, signatures, this); } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericPostWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericPostWorker.java index 8172e14..a720eda 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericPostWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericPostWorker.java @@ -35,14 +35,9 @@ public abstract class MultiServerGenericPostWorker extends MultiServerWorker< public void run() { // Iterate through servers - - Iterator clientIterator = getClientIterator(); - - while (clientIterator.hasNext()) { + for (SingleServerBulletinBoardClient client : clients) { // Send request to Server - SingleServerBulletinBoardClient client = clientIterator.next(); - doPost(client, payload); } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericReadWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericReadWorker.java index 88b4ac1..f17708b 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericReadWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericReadWorker.java @@ -14,7 +14,9 @@ import java.util.List; */ public abstract class MultiServerGenericReadWorker extends MultiServerWorker{ - private final Iterator clientIterator; + private Iterator clientIterator; + + private String errorString; public MultiServerGenericReadWorker(List clients, int minServers, IN payload, int maxRetry, @@ -22,7 +24,8 @@ public abstract class MultiServerGenericReadWorker extends MultiServerW super(clients, true, minServers, payload, maxRetry, futureCallback); // Shuffle clients on creation to balance load - clientIterator = getClientIterator(); + clientIterator = clients.iterator(); + errorString = ""; } @@ -46,7 +49,7 @@ public abstract class MultiServerGenericReadWorker extends MultiServerW doRead(payload, client); } else { - fail(new CommunicationException("Could not contact any server")); + fail(new CommunicationException("Could not contact any server. Errors follow:\n" + errorString)); } } @@ -58,6 +61,8 @@ public abstract class MultiServerGenericReadWorker extends MultiServerW @Override public void onFailure(Throwable t) { + //TODO: log + errorString += t.getCause() + " " + t.getMessage() + "\n"; run(); // Retry with next server } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGetRedundancyWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGetRedundancyWorker.java index 748916b..b76a94f 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGetRedundancyWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGetRedundancyWorker.java @@ -36,13 +36,8 @@ public class MultiServerGetRedundancyWorker extends MultiServerWorker clientIterator = getClientIterator(); - // Iterate through clients - - while (clientIterator.hasNext()) { - - SingleServerBulletinBoardClient client = clientIterator.next(); + for (SingleServerBulletinBoardClient client : clients) { // Send request to client client.getRedundancy(payload,this); diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchDataWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchDataWorker.java index ca7b4fd..052b625 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchDataWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchDataWorker.java @@ -1,15 +1,18 @@ package meerkat.bulletinboard.workers.multiserver; import com.google.common.util.concurrent.FutureCallback; +import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; +import meerkat.bulletinboard.MultiServerWorker; import meerkat.bulletinboard.SingleServerBulletinBoardClient; import meerkat.bulletinboard.BatchDataContainer; +import java.util.Iterator; import java.util.List; /** * Created by Arbel Deutsch Peled on 27-Dec-15. */ -public class MultiServerPostBatchDataWorker extends MultiServerGenericPostWorker { +public class MultiServerPostBatchDataWorker extends MultiServerWorker { public MultiServerPostBatchDataWorker(List clients, int minServers, BatchDataContainer payload, int maxRetry, @@ -20,9 +23,50 @@ public class MultiServerPostBatchDataWorker extends MultiServerGenericPostWorker } @Override - protected void doPost(SingleServerBulletinBoardClient client, BatchDataContainer payload) { - client.postBatchData(payload.signerId, payload.batchId, payload.batchChunkList, payload.startPosition, this); + public void run() { + + Iterator identifierIterator = payload.batchId.getIdentifiers().iterator(); + + // Iterate through client + + for (SingleServerBulletinBoardClient client : clients) { + + if (identifierIterator.hasNext()) { + + // Fetch the batch identifier supplied by the specific client (may be null if batch open failed on client + + BatchIdentifier identifier = identifierIterator.next(); + + if (identifier != null) { + + // Post the data with the matching identifier to the client + client.postBatchData(identifierIterator.next(), payload.batchChunkList, payload.startPosition, this); + + } else { + + // Count servers with no batch identifier as failed + maxFailedServers.decrementAndGet(); + + } + + } + + } + } + @Override + public void onSuccess(Boolean result) { + if (minServers.decrementAndGet() <= 0){ + succeed(result); + } + } + + @Override + public void onFailure(Throwable t) { + if (maxFailedServers.decrementAndGet() <= 0){ + fail(t); + } + } } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchWorker.java index 5b5198c..b938f52 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchWorker.java @@ -11,17 +11,23 @@ import java.util.List; */ public class MultiServerPostBatchWorker extends MultiServerGenericPostWorker { + private final int chunkSize; + public MultiServerPostBatchWorker(List clients, - int minServers, BulletinBoardMessage payload, int maxRetry, + int minServers, BulletinBoardMessage payload, int chunkSize, int maxRetry, FutureCallback futureCallback) { super(clients, minServers, payload, maxRetry, futureCallback); + this.chunkSize = chunkSize; + } @Override protected void doPost(SingleServerBulletinBoardClient client, BulletinBoardMessage payload) { - client.postBatch(payload, this); + + client.postAsBatch(payload, chunkSize, this); + } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java new file mode 100644 index 0000000..959c60f --- /dev/null +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java @@ -0,0 +1,29 @@ +package meerkat.bulletinboard.workers.multiserver; + +import com.google.common.util.concurrent.FutureCallback; +import meerkat.bulletinboard.SingleServerBulletinBoardClient; +import meerkat.protobuf.BulletinBoardAPI.*; + +import java.util.List; + + +/** + * Created by Arbel Deutsch Peled on 27-Dec-15. + */ +public class MultiServerReadBatchDataWorker extends MultiServerGenericReadWorker { + + public MultiServerReadBatchDataWorker(List clients, + int minServers, MessageID payload, int maxRetry, + FutureCallback futureCallback) { + + super(clients, minServers, payload, maxRetry, futureCallback); + + } + + @Override + protected void doRead(MessageID payload, SingleServerBulletinBoardClient client) { + client.readBatch(payload, this); + } + + +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchWorker.java index db58247..59b4ce5 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchWorker.java @@ -3,7 +3,7 @@ package meerkat.bulletinboard.workers.multiserver; import com.google.common.util.concurrent.FutureCallback; import meerkat.bulletinboard.SingleServerBulletinBoardClient; import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage; -import meerkat.protobuf.BulletinBoardAPI.BatchSpecificationMessage; +import meerkat.protobuf.BulletinBoardAPI.MessageID; import java.util.List; @@ -11,10 +11,10 @@ import java.util.List; /** * Created by Arbel Deutsch Peled on 27-Dec-15. */ -public class MultiServerReadBatchWorker extends MultiServerGenericReadWorker { +public class MultiServerReadBatchWorker extends MultiServerGenericReadWorker { public MultiServerReadBatchWorker(List clients, - int minServers, BatchSpecificationMessage payload, int maxRetry, + int minServers, MessageID payload, int maxRetry, FutureCallback futureCallback) { super(clients, minServers, payload, maxRetry, futureCallback); @@ -22,7 +22,7 @@ public class MultiServerReadBatchWorker extends MultiServerGenericReadWorker { +public class SingleServerBeginBatchWorker extends SingleServerWorker { public SingleServerBeginBatchWorker(String serverAddress, BeginBatchMessage payload, int maxRetry) { - super(serverAddress, BEGIN_BATCH_PATH, payload, maxRetry); + super(serverAddress, payload, maxRetry); } + @Override + public Int64Value call() throws Exception { + Client client = clientLocal.get(); + + WebTarget webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(BEGIN_BATCH_PATH); + Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post( + Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF)); + + try { + + return response.readEntity(Int64Value.class); + + } catch (ProcessingException | IllegalStateException e) { + + // Post to this server failed + throw new CommunicationException("Could not contact the server"); + + } + finally { + response.close(); + } + } } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerReadBatchWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerReadBatchWorker.java index ee2e193..8bc4bcd 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerReadBatchWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerReadBatchWorker.java @@ -17,14 +17,12 @@ import java.util.List; import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH; import static meerkat.bulletinboard.BulletinBoardConstants.READ_BATCH_PATH; -import static meerkat.bulletinboard.BulletinBoardConstants.BATCH_ID_TAG_PREFIX; - /** * Created by Arbel Deutsch Peled on 27-Dec-15. */ -public class SingleServerReadBatchWorker extends SingleServerWorker> { +public class SingleServerReadBatchWorker extends SingleServerWorker> { - public SingleServerReadBatchWorker(String serverAddress, BatchSpecificationMessage payload, int maxRetry) { + public SingleServerReadBatchWorker(String serverAddress, BatchQuery payload, int maxRetry) { super(serverAddress, payload, maxRetry); } diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java index 6661491..dcea3e7 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java @@ -118,58 +118,43 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ new int[] {} ), - GET_BATCH_MESSAGE_ENTRY( - new String[] {"SignerId", "BatchId"}, - new int[] {Types.BLOB, Types.INTEGER} - ), - CHECK_BATCH_LENGTH( - new String[] {"SignerId", "BatchId"}, + new String[] {"BatchId"}, new int[] {Types.BLOB, Types.INTEGER} ), CHECK_BATCH_OPEN( - new String[] {"SignerId", "BatchId"}, + new String[] {"BatchId"}, new int[] {Types.BLOB, Types.INTEGER} ), - GET_BATCH_MESSAGE_DATA( + GET_BATCH_MESSAGE_DATA_BY_MSG_ID( new String[] {"MsgId", "StartPosition"}, new int[] {Types.BLOB, Types.INTEGER} ), - GET_BATCH_MESSAGE_DATA_BY_IDS( - new String[] {"SignerId", "BatchId", "StartPosition"}, - new int[] {Types.BLOB, Types.INTEGER, Types.INTEGER} + GET_BATCH_MESSAGE_DATA_BY_BATCH_ID( + new String[] {"BatchId", "StartPosition"}, + new int[] {Types.INTEGER, Types.INTEGER} ), INSERT_BATCH_DATA( - new String[] {"SignerId", "BatchId", "SerialNum", "Data"}, - new int[] {Types.BLOB, Types.INTEGER, Types.INTEGER, Types.BLOB} + new String[] {"BatchId", "SerialNum", "Data"}, + new int[] {Types.INTEGER, Types.INTEGER, Types.BLOB} ), STORE_BATCH_TAGS( - new String[] {"SignerId", "BatchId", "Tags"}, - new int[] {Types.BLOB, Types.INTEGER, Types.BLOB} + new String[] {"Tags"}, + new int[] {Types.BLOB} ), GET_BATCH_TAGS( - new String[] {"SignerId", "BatchId"}, - new int[] {Types.BLOB, Types.INTEGER} - ), - - REMOVE_BATCH_TAGS( - new String[] {"SignerId", "BatchId"}, - new int[] {Types.BLOB, Types.INTEGER} - ), - - REMOVE_BATCH_IDS( - new String[] {"SignerId", "BatchId"}, + new String[] {"BatchId"}, new int[] {Types.BLOB, Types.INTEGER} ), ADD_ENTRY_NUM_TO_BATCH( - new String[] {"SignerId", "BatchId", "EntryNum"}, + new String[] {"BatchId", "EntryNum"}, new int[] {Types.BLOB, Types.INTEGER, Types.INTEGER} ); @@ -783,23 +768,21 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ return false; } - return messages.get(0).getMsg().getIsStub(); + return (messages.get(0).getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.MSGID); } /** * This method checks if a specified batch exists and is still open - * @param signerId is the signer ID - * @param batchId is the batch ID + * @param batchId is the temporary batch ID * @return TRUE if the batch is closed and FALSE if it is still open or doesn't exist at all */ - private boolean isBatchOpen(ByteString signerId, int batchId) throws CommunicationException { + private boolean isBatchOpen(long batchId) throws CommunicationException { String sql = sqlQueryProvider.getSQLString(QueryType.CHECK_BATCH_OPEN); MapSqlParameterSource namedParameters = new MapSqlParameterSource(); - namedParameters.addValue(QueryType.CHECK_BATCH_OPEN.getParamName(0),signerId.toByteArray()); - namedParameters.addValue(QueryType.CHECK_BATCH_OPEN.getParamName(1),batchId); + namedParameters.addValue(QueryType.CHECK_BATCH_OPEN.getParamName(0),batchId); List result = jdbcTemplate.query(sql, namedParameters, new LongMapper()); @@ -808,24 +791,22 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ } @Override - public BoolValue beginBatch(BeginBatchMessage message) throws CommunicationException { - - // Check if batch is already open - if (isBatchOpen(message.getSignerId(), message.getBatchId())) { - return BoolValue.newBuilder().setValue(false).build(); - } + public Int64Value beginBatch(BeginBatchMessage message) throws CommunicationException { // Store tags String sql = sqlQueryProvider.getSQLString(QueryType.STORE_BATCH_TAGS); MapSqlParameterSource namedParameters = new MapSqlParameterSource(); - namedParameters.addValue(QueryType.STORE_BATCH_TAGS.getParamName(0),message.getSignerId().toByteArray()); - namedParameters.addValue(QueryType.STORE_BATCH_TAGS.getParamName(1),message.getBatchId()); - namedParameters.addValue(QueryType.STORE_BATCH_TAGS.getParamName(2),message.toByteArray()); + namedParameters.addValue(QueryType.STORE_BATCH_TAGS.getParamName(0),message.toByteArray()); jdbcTemplate.update(sql,namedParameters); - return BoolValue.newBuilder().setValue(true).build(); + KeyHolder keyHolder = new GeneratedKeyHolder(); + jdbcTemplate.update(sql, namedParameters, keyHolder); + + long entryNum = keyHolder.getKey().longValue(); + + return Int64Value.newBuilder().setValue(entryNum).build(); } @@ -834,7 +815,7 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ public BoolValue postBatchMessage(BatchMessage batchMessage) throws CommunicationException{ // Make sure batch is open - if (!isBatchOpen(batchMessage.getSignerId(), batchMessage.getBatchId())) { + if (!isBatchOpen(batchMessage.getBatchId())) { return BoolValue.newBuilder().setValue(false).build(); } @@ -842,10 +823,9 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ String sql = sqlQueryProvider.getSQLString(QueryType.INSERT_BATCH_DATA); MapSqlParameterSource namedParameters = new MapSqlParameterSource(); - namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(0),batchMessage.getSignerId().toByteArray()); - namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(1),batchMessage.getBatchId()); - namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(2),batchMessage.getSerialNum()); - namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(3),batchMessage.getData().toByteArray()); + namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(0),batchMessage.getBatchId()); + namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(1),batchMessage.getSerialNum()); + namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(2),batchMessage.getData().toByteArray()); jdbcTemplate.update(sql, namedParameters); @@ -857,8 +837,6 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ @Override public BoolValue closeBatch(CloseBatchMessage message) throws CommunicationException { - ByteString signerId = message.getSig(0).getSignerId(); - int batchId = message.getBatchId(); // Check batch size @@ -866,8 +844,7 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ MapSqlParameterSource namedParameters = new MapSqlParameterSource(); - namedParameters.addValue(QueryType.CHECK_BATCH_LENGTH.getParamName(0),signerId.toByteArray()); - namedParameters.addValue(QueryType.CHECK_BATCH_LENGTH.getParamName(1),batchId); + namedParameters.addValue(QueryType.CHECK_BATCH_LENGTH.getParamName(0),message.getBatchId()); List lengthResult = jdbcTemplate.query(sql, namedParameters, new LongMapper()); @@ -880,8 +857,7 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_TAGS); namedParameters = new MapSqlParameterSource(); - namedParameters.addValue(QueryType.GET_BATCH_TAGS.getParamName(0),signerId.toByteArray()); - namedParameters.addValue(QueryType.GET_BATCH_TAGS.getParamName(1),batchId); + namedParameters.addValue(QueryType.GET_BATCH_TAGS.getParamName(0),message.getBatchId()); List beginBatchMessages = jdbcTemplate.query(sql, namedParameters, new BeginBatchMessageMapper()); @@ -892,7 +868,6 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ UnsignedBulletinBoardMessage unsignedMessage = UnsignedBulletinBoardMessage.newBuilder() .addAllTag(beginBatchMessages.get(0).getTagList()) .setTimestamp(message.getTimestamp()) - .setIsStub(true) .build(); // Digest the data @@ -900,12 +875,11 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ digest.reset(); digest.update(unsignedMessage); - sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS); + sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID); namedParameters = new MapSqlParameterSource(); - namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(0),signerId.toByteArray()); - namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(1),batchId); - namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(2),0); // Read from the beginning + namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(0),message.getBatchId()); + namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(1),0); // Read from the beginning jdbcTemplate.query(sql, namedParameters, new BatchDataDigestHandler(digest)); byte[] msgID = digest.digest(); @@ -914,33 +888,21 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ // Create Bulletin Board message BulletinBoardMessage bulletinBoardMessage = BulletinBoardMessage.newBuilder() - .setMsg(unsignedMessage) + .setMsg(UnsignedBulletinBoardMessage.newBuilder() + .mergeFrom(unsignedMessage) + .setMsgId(ByteString.copyFrom(msgID))) .addAllSig(message.getSigList()) .build(); // Post message with pre-calculated ID and without checking signature validity long entryNum = postMessage(bulletinBoardMessage, msgID); - // Remove signer ID and batch ID from batch data table - // This allows reuse of the batch ID in subsequent batches - - sql = sqlQueryProvider.getSQLString(QueryType.REMOVE_BATCH_IDS); - - namedParameters = new MapSqlParameterSource(); - - namedParameters.addValue(QueryType.REMOVE_BATCH_IDS.getParamName(0), signerId.toByteArray()); - namedParameters.addValue(QueryType.REMOVE_BATCH_IDS.getParamName(1), batchId); - - jdbcTemplate.update(sql, namedParameters); - - // Remove tags from temporary table - sql = sqlQueryProvider.getSQLString(QueryType.REMOVE_BATCH_TAGS); - - jdbcTemplate.update(sql, namedParameters); - // Add entry num to tag data table sql = sqlQueryProvider.getSQLString(QueryType.ADD_ENTRY_NUM_TO_BATCH); - namedParameters.addValue(QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(2), entryNum); + namedParameters = new MapSqlParameterSource(); + + namedParameters.addValue(QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(0), message.getBatchId()); + namedParameters.addValue(QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1), entryNum); jdbcTemplate.update(sql, namedParameters); @@ -959,11 +921,11 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ throw new IllegalArgumentException("No such batch"); } - String sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA); + String sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID); MapSqlParameterSource namedParameters = new MapSqlParameterSource(); - namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),batchQuery.getMsgID().getID().toByteArray()); - namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),batchQuery.getStartPosition()); + namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(0),batchQuery.getMsgID().getID().toByteArray()); + namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(1),batchQuery.getStartPosition()); jdbcTemplate.query(sql, namedParameters, new BatchDataCallbackHandler(out)); @@ -1031,7 +993,7 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ checksumChanged = true; - checksum.update(message.getMsg().getData()); + checksum.update(message.getMsg().getMsgId()); lastTimestamp = message.getMsg().getTimestamp(); message = messageIterator.next(); @@ -1109,7 +1071,7 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ // Advance checksum - ByteString messageID = message.getMsg().getData(); // The data field contains the message ID + ByteString messageID = message.getMsg().getMsgId(); // The data field contains the message ID checksum.update(messageID); diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java index 2f31e9e..872e226 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java @@ -94,90 +94,55 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider case GET_LAST_MESSAGE_ENTRY: return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable"; - case GET_BATCH_MESSAGE_ENTRY: - return MessageFormat.format( - "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable" - + " INNER JOIN SignatureTable ON MsgTable.EntryNum = SignatureTable.EntryNum" - + " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum" - + " INNER JOIN TagTable ON MsgTagTable.TagId = TagTable.TagId" - + " WHERE SignatureTable.SignerId = :{0}" - + " AND TagTable.Tag = :{1}", - QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0), - QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1)); - - case GET_BATCH_MESSAGE_DATA: + case GET_BATCH_MESSAGE_DATA_BY_MSG_ID: return MessageFormat.format( "SELECT Data FROM BatchTable" + " INNER JOIN MsgTable ON MsgTable.EntryNum = BatchTable.EntryNum" + " WHERE MsgTable.MsgId = :{0} AND BatchTable.SerialNum >= :{1}" + " ORDER BY BatchTable.SerialNum ASC", - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0), - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1)); + QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(0), + QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(1)); - case GET_BATCH_MESSAGE_DATA_BY_IDS: + case GET_BATCH_MESSAGE_DATA_BY_BATCH_ID: return MessageFormat.format( "SELECT Data FROM BatchTable" - + " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}" + + " WHERE BatchId = :{0} AND SerialNum >= :{1}" + " ORDER BY BatchTable.SerialNum ASC", - QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(0), - QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(1), - QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(2)); + QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(0), + QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(1)); case INSERT_BATCH_DATA: return MessageFormat.format( - "INSERT INTO BatchTable (SignerId, BatchId, SerialNum, Data)" - + " VALUES (:{0}, :{1}, :{2}, :{3})", + "INSERT INTO BatchTable (BatchId, SerialNum, Data) VALUES (:{0}, :{1}, :{2})", QueryType.INSERT_BATCH_DATA.getParamName(0), QueryType.INSERT_BATCH_DATA.getParamName(1), - QueryType.INSERT_BATCH_DATA.getParamName(2), - QueryType.INSERT_BATCH_DATA.getParamName(3)); + QueryType.INSERT_BATCH_DATA.getParamName(2)); case CHECK_BATCH_LENGTH: return MessageFormat.format( - "SELECT COUNT(Data) AS BatchLength FROM BatchTable" - + " WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.CHECK_BATCH_LENGTH.getParamName(0), - QueryType.CHECK_BATCH_LENGTH.getParamName(1)); + "SELECT COUNT(Data) AS BatchLength FROM BatchTable WHERE BatchId = :{0}", + QueryType.CHECK_BATCH_LENGTH.getParamName(0)); case CHECK_BATCH_OPEN: return MessageFormat.format( - "SELECT COUNT(SignerId) AS signCount FROM BatchTagTable" - + " WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.CHECK_BATCH_OPEN.getParamName(0), - QueryType.CHECK_BATCH_OPEN.getParamName(1)); + "SELECT COUNT(BatchId) AS batchCount FROM BatchTagTable WHERE BatchId = :{0}", + QueryType.CHECK_BATCH_OPEN.getParamName(0)); case STORE_BATCH_TAGS: return MessageFormat.format( - "INSERT INTO BatchTagTable (SignerId, BatchId, Tags) VALUES (:{0}, :{1}, :{2})", - QueryType.STORE_BATCH_TAGS.getParamName(0), - QueryType.STORE_BATCH_TAGS.getParamName(1), - QueryType.STORE_BATCH_TAGS.getParamName(2)); + "INSERT INTO BatchTagTable (Tags) VALUES (:{0})", + QueryType.STORE_BATCH_TAGS.getParamName(0)); case GET_BATCH_TAGS: return MessageFormat.format( - "SELECT Tags FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.GET_BATCH_TAGS.getParamName(0), - QueryType.GET_BATCH_TAGS.getParamName(1)); - - case REMOVE_BATCH_TAGS: - return MessageFormat.format( - "DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.REMOVE_BATCH_TAGS.getParamName(0), - QueryType.REMOVE_BATCH_TAGS.getParamName(1)); - - case REMOVE_BATCH_IDS: - return MessageFormat.format( - "UPDATE BatchTable Set (SignerId, BatchId) = (NULL, NULL)" + - " WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.REMOVE_BATCH_IDS.getParamName(0), - QueryType.REMOVE_BATCH_IDS.getParamName(1)); + "SELECT Tags FROM BatchTagTable WHERE BatchId = :{0}", + QueryType.GET_BATCH_TAGS.getParamName(0)); case ADD_ENTRY_NUM_TO_BATCH: return MessageFormat.format( - "UPDATE BatchTable SET EntryNum = :{2} WHERE SignerId = :{0} AND BatchId = :{1}", + "UPDATE BatchTable SET EntryNum = :{1} WHERE BatchId = :{0}", QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(0), - QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1), - QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(2)); + QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1)); default: throw new IllegalArgumentException("Cannot serve a query of type " + queryType); @@ -266,7 +231,8 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider public List getSchemaCreationCommands() { List list = new LinkedList(); - list.add("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INT NOT NULL AUTO_INCREMENT PRIMARY KEY, MsgId TINYBLOB UNIQUE, ExactTime TIMESTAMP, Msg BLOB)"); + list.add("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INT NOT NULL AUTO_INCREMENT PRIMARY KEY," + + " MsgId TINYBLOB UNIQUE, ExactTime TIMESTAMP, Msg BLOB)"); list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INT NOT NULL AUTO_INCREMENT PRIMARY KEY, Tag VARCHAR(50) UNIQUE)"); @@ -281,12 +247,14 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)"); list.add("CREATE UNIQUE INDEX IF NOT EXISTS SignatureIndex ON SignatureTable(SignerId, EntryNum)"); - list.add("CREATE TABLE IF NOT EXISTS BatchTable (EntryNum INT, SignerId TINYBLOB, BatchId INT, SerialNum INT, Data BLOB," - + " UNIQUE(SignerId, BatchId, SerialNum))"); + list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (BatchId INT AUTO_INCREMENT PRIMARY KEY, Tags BLOB)"); + + list.add("CREATE TABLE IF NOT EXISTS BatchTable (BatchId INT, EntryNum INT, SerialNum INT, Data BLOB," + + " UNIQUE(BatchId, SerialNum)," + + " FOREIGN KEY (BatchId) REFERENCES BatchTagTable(BatchId) ON DELETE CASCADE)"); list.add("CREATE INDEX IF NOT EXISTS BatchDataIndex ON BatchTable(EntryNum, SerialNum)"); - list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, Tags BLOB)"); - list.add("CREATE UNIQUE INDEX IF NOT EXISTS BatchTagIndex ON BatchTagTable(SignerId, BatchId)"); + // This is used to create a simple table with one entry. // It is used for implementing a workaround for the missing INSERT IGNORE syntax @@ -302,12 +270,12 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider list.add("DROP TABLE IF EXISTS UtilityTable"); - list.add("DROP INDEX IF EXISTS BatchTagIndex"); - list.add("DROP TABLE IF EXISTS BatchTagTable"); - list.add("DROP INDEX IF EXISTS BatchDataIndex"); list.add("DROP TABLE IF EXISTS BatchTable"); + list.add("DROP INDEX IF EXISTS BatchTagIndex"); + list.add("DROP TABLE IF EXISTS BatchTagTable"); + list.add("DROP TABLE IF EXISTS MsgTagTable"); list.add("DROP INDEX IF EXISTS SignerIdIndex"); diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java index 2931a34..097095f 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java @@ -97,90 +97,55 @@ public class MySQLQueryProvider implements SQLQueryProvider { case GET_LAST_MESSAGE_ENTRY: return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable"; - case GET_BATCH_MESSAGE_ENTRY: - return MessageFormat.format( - "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable" - + " INNER JOIN SignatureTable ON MsgTable.EntryNum = SignatureTable.EntryNum" - + " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum" - + " INNER JOIN TagTable ON MsgTagTable.TagId = TagTable.TagId" - + " WHERE SignatureTable.SignerId = :{0}" - + " AND TagTable.Tag = :{1}", - QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0), - QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1)); - - case GET_BATCH_MESSAGE_DATA: + case GET_BATCH_MESSAGE_DATA_BY_MSG_ID: return MessageFormat.format( "SELECT Data FROM BatchTable" + " INNER JOIN MsgTable ON MsgTable.EntryNum = BatchTable.EntryNum" + " WHERE MsgTable.MsgId = :{0} AND BatchTable.SerialNum >= :{1}" + " ORDER BY BatchTable.SerialNum ASC", - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0), - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1)); + QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(0), + QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(1)); - case GET_BATCH_MESSAGE_DATA_BY_IDS: + case GET_BATCH_MESSAGE_DATA_BY_BATCH_ID: return MessageFormat.format( "SELECT Data FROM BatchTable" - + " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}" + + " WHERE BatchId = :{0} AND SerialNum >= :{1}" + " ORDER BY BatchTable.SerialNum ASC", - QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(0), - QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(1), - QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(2)); + QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(0), + QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(1)); case INSERT_BATCH_DATA: return MessageFormat.format( - "INSERT INTO BatchTable (SignerId, BatchId, SerialNum, Data)" - + " VALUES (:{0}, :{1}, :{2}, :{3})", + "INSERT INTO BatchTable (BatchId, SerialNum, Data) VALUES (:{0}, :{1}, :{2})", QueryType.INSERT_BATCH_DATA.getParamName(0), QueryType.INSERT_BATCH_DATA.getParamName(1), - QueryType.INSERT_BATCH_DATA.getParamName(2), - QueryType.INSERT_BATCH_DATA.getParamName(3)); + QueryType.INSERT_BATCH_DATA.getParamName(2)); case CHECK_BATCH_LENGTH: return MessageFormat.format( - "SELECT COUNT(Data) AS BatchLength FROM BatchTable" - + " WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.CHECK_BATCH_LENGTH.getParamName(0), - QueryType.CHECK_BATCH_LENGTH.getParamName(1)); + "SELECT COUNT(Data) AS BatchLength FROM BatchTable WHERE BatchId = :{0}", + QueryType.CHECK_BATCH_LENGTH.getParamName(0)); case CHECK_BATCH_OPEN: return MessageFormat.format( - "SELECT COUNT(Tags) AS signCount FROM BatchTagTable" - + " WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.CHECK_BATCH_OPEN.getParamName(0), - QueryType.CHECK_BATCH_OPEN.getParamName(1)); + "SELECT COUNT(BatchId) AS batchCount FROM BatchTagTable WHERE BatchId = :{0}", + QueryType.CHECK_BATCH_OPEN.getParamName(0)); case STORE_BATCH_TAGS: return MessageFormat.format( - "INSERT INTO BatchTagTable (SignerId, BatchId, Tags) VALUES (:{0}, :{1}, :{2})", - QueryType.STORE_BATCH_TAGS.getParamName(0), - QueryType.STORE_BATCH_TAGS.getParamName(1), - QueryType.STORE_BATCH_TAGS.getParamName(2)); + "INSERT INTO BatchTagTable (Tags) VALUES (:{0})", + QueryType.STORE_BATCH_TAGS.getParamName(0)); case GET_BATCH_TAGS: return MessageFormat.format( - "SELECT Tags FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.GET_BATCH_TAGS.getParamName(0), - QueryType.GET_BATCH_TAGS.getParamName(1)); - - case REMOVE_BATCH_TAGS: - return MessageFormat.format( - "DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.REMOVE_BATCH_TAGS.getParamName(0), - QueryType.REMOVE_BATCH_TAGS.getParamName(1)); - - case REMOVE_BATCH_IDS: - return MessageFormat.format( - "UPDATE BatchTable Set SignerId = NULL, BatchId = NULL" + - " WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.REMOVE_BATCH_IDS.getParamName(0), - QueryType.REMOVE_BATCH_IDS.getParamName(1)); + "SELECT Tags FROM BatchTagTable WHERE BatchId = :{0}", + QueryType.GET_BATCH_TAGS.getParamName(0)); case ADD_ENTRY_NUM_TO_BATCH: return MessageFormat.format( - "UPDATE BatchTable SET EntryNum = :{2} WHERE SignerId = :{0} AND BatchId = :{1}", + "UPDATE BatchTable SET EntryNum = :{1} WHERE BatchId = :{0}", QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(0), - QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1), - QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(2)); + QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1)); default: throw new IllegalArgumentException("Cannot serve a query of type " + queryType); @@ -276,19 +241,21 @@ public class MySQLQueryProvider implements SQLQueryProvider { list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INT NOT NULL AUTO_INCREMENT PRIMARY KEY, Tag VARCHAR(50), UNIQUE(Tag))"); list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum INT, TagId INT," - + " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum)," - + " CONSTRAINT FOREIGN KEY (TagId) REFERENCES TagTable(TagId)," + + " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE," + + " CONSTRAINT FOREIGN KEY (TagId) REFERENCES TagTable(TagId) ON DELETE CASCADE," + " CONSTRAINT UNIQUE (EntryNum, TagID))"); list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INT, SignerId TINYBLOB, Signature TINYBLOB," + " INDEX(SignerId(32)), CONSTRAINT Unique_Signature UNIQUE(SignerId(32), EntryNum)," - + " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))"); + + " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE)"); + + list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (BatchId INT AUTO_INCREMENT PRIMARY KEY, Tags BLOB)"); + + list.add("CREATE TABLE IF NOT EXISTS BatchTable (BatchId INT, EntryNum INT, SerialNum INT, Data BLOB," + + " CONSTRAINT UNIQUE(BatchId, SerialNum)," + + " CONSTRAINT FOREIGN KEY (BatchId) REFERENCES BatchTagTable(BatchId) ON DELETE CASCADE)"); - list.add("CREATE TABLE IF NOT EXISTS BatchTable (EntryNum INT, SignerId TINYBLOB, BatchId INT, SerialNum INT, Data BLOB," - + " CONSTRAINT Unique_Batch UNIQUE(SignerId(32), BatchId, SerialNum))"); - list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, Tags BLOB," - + " INDEX(SignerId(32), BatchId))"); return list; } @@ -297,8 +264,8 @@ public class MySQLQueryProvider implements SQLQueryProvider { public List getSchemaDeletionCommands() { List list = new LinkedList(); - list.add("DROP TABLE IF EXISTS BatchTagTable"); list.add("DROP TABLE IF EXISTS BatchTable"); + list.add("DROP TABLE IF EXISTS BatchTagTable"); list.add("DROP TABLE IF EXISTS MsgTagTable"); list.add("DROP TABLE IF EXISTS SignatureTable"); list.add("DROP TABLE IF EXISTS TagTable"); diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/SQLiteQueryProvider.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/SQLiteQueryProvider.java index 0462543..9f68955 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/SQLiteQueryProvider.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/SQLiteQueryProvider.java @@ -1,6 +1,7 @@ package meerkat.bulletinboard.sqlserver; import meerkat.protobuf.BulletinBoardAPI.*; +import org.apache.commons.dbcp2.BasicDataSource; import org.sqlite.SQLiteDataSource; import javax.sql.DataSource; @@ -60,91 +61,55 @@ public class SQLiteQueryProvider implements BulletinBoardSQLServer.SQLQueryProvi case GET_LAST_MESSAGE_ENTRY: return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable"; - case GET_BATCH_MESSAGE_ENTRY: - return MessageFormat.format( - "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable" - + " INNER JOIN SignatureTable ON MsgTable.EntryNum = SignatureTable.EntryNum" - + " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum" - + " INNER JOIN TagTable ON MsgTagTable.TagId = TagTable.TagId" - + " WHERE SignatureTable.SignerId = :{0}" - + " AND TagTable.Tag = :{1}", - QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0), - QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1)); - - case GET_BATCH_MESSAGE_DATA: + case GET_BATCH_MESSAGE_DATA_BY_MSG_ID: return MessageFormat.format( "SELECT Data FROM BatchTable" + " INNER JOIN MsgTable ON MsgTable.EntryNum = BatchTable.EntryNum" + " WHERE MsgTable.MsgId = :{0} AND BatchTable.SerialNum >= :{1}" + " ORDER BY BatchTable.SerialNum ASC", - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0), - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1)); + QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(0), + QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(1)); - case GET_BATCH_MESSAGE_DATA_BY_IDS: + case GET_BATCH_MESSAGE_DATA_BY_BATCH_ID: return MessageFormat.format( "SELECT Data FROM BatchTable" - + " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}" + + " WHERE BatchId = :{0} AND SerialNum >= :{1}" + " ORDER BY BatchTable.SerialNum ASC", - QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(0), - QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(1), - QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(2)); + QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(0), + QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(1)); case INSERT_BATCH_DATA: return MessageFormat.format( - "INSERT INTO BatchTable (SignerId, BatchId, SerialNum, Data)" - + " VALUES (:{0}, :{1}, :{2}, :{3})", + "INSERT INTO BatchTable (BatchId, SerialNum, Data) VALUES (:{0}, :{1}, :{2})", QueryType.INSERT_BATCH_DATA.getParamName(0), QueryType.INSERT_BATCH_DATA.getParamName(1), - QueryType.INSERT_BATCH_DATA.getParamName(2), - QueryType.INSERT_BATCH_DATA.getParamName(3)); + QueryType.INSERT_BATCH_DATA.getParamName(2)); case CHECK_BATCH_LENGTH: return MessageFormat.format( - "SELECT COUNT(Data) AS BatchLength FROM BatchTable" - + " WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.CHECK_BATCH_LENGTH.getParamName(0), - QueryType.CHECK_BATCH_LENGTH.getParamName(1)); + "SELECT COUNT(Data) AS BatchLength FROM BatchTable WHERE BatchId = :{0}", + QueryType.CHECK_BATCH_LENGTH.getParamName(0)); case CHECK_BATCH_OPEN: return MessageFormat.format( - "SELECT COUNT(Tags) AS signCount FROM BatchTagTable" - + " WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.CHECK_BATCH_OPEN.getParamName(0), - QueryType.CHECK_BATCH_OPEN.getParamName(1)); + "SELECT COUNT(BatchId) AS batchCount FROM BatchTagTable WHERE BatchId = :{0}", + QueryType.CHECK_BATCH_OPEN.getParamName(0)); case STORE_BATCH_TAGS: return MessageFormat.format( - "INSERT INTO BatchTagTable (SignerId, BatchId, Tags) VALUES (:{0}, :{1}, :{2})", - QueryType.STORE_BATCH_TAGS.getParamName(0), - QueryType.STORE_BATCH_TAGS.getParamName(1), - QueryType.STORE_BATCH_TAGS.getParamName(2)); + "INSERT INTO BatchTagTable (Tags) VALUES (:{0})", + QueryType.STORE_BATCH_TAGS.getParamName(0)); case GET_BATCH_TAGS: return MessageFormat.format( - "SELECT Tag FROM TagTable INNER JOIN BatchTagTable ON TagTable.TagId = BatchTagTable.TagId" - + " WHERE SignerId = :{0} AND BatchId = :{1} ORDER BY Tag ASC", - QueryType.GET_BATCH_TAGS.getParamName(0), - QueryType.GET_BATCH_TAGS.getParamName(1)); - - case REMOVE_BATCH_TAGS: - return MessageFormat.format( - "DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.REMOVE_BATCH_TAGS.getParamName(0), - QueryType.REMOVE_BATCH_TAGS.getParamName(1)); - - case REMOVE_BATCH_IDS: - return MessageFormat.format( - "UPDATE BatchTable Set (SignerId, BatchId) = (NULL, NULL)" + - " WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.REMOVE_BATCH_IDS.getParamName(0), - QueryType.REMOVE_BATCH_IDS.getParamName(1)); + "SELECT Tags FROM BatchTagTable WHERE BatchId = :{0}", + QueryType.GET_BATCH_TAGS.getParamName(0)); case ADD_ENTRY_NUM_TO_BATCH: return MessageFormat.format( - "UPDATE BatchTable SET EntryNum = :{2} WHERE SignerId = :{0} AND BatchId = :{1}", + "UPDATE BatchTable SET EntryNum = :{1} WHERE BatchId = :{0}", QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(0), - QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1), - QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(2)); + QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1)); default: throw new IllegalArgumentException("Cannot serve a query of type " + queryType); @@ -220,7 +185,8 @@ public class SQLiteQueryProvider implements BulletinBoardSQLServer.SQLQueryProvi @Override public DataSource getDataSource() { - SQLiteDataSource dataSource = new SQLiteDataSource(); + BasicDataSource dataSource = new BasicDataSource(); + dataSource.setDriverClassName("org.sqlite.JDBC"); dataSource.setUrl("jdbc:sqlite:" + dbName); return dataSource; @@ -234,14 +200,23 @@ public class SQLiteQueryProvider implements BulletinBoardSQLServer.SQLQueryProvi list.add("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INTEGER PRIMARY KEY, MsgId BLOB UNIQUE, Msg BLOB)"); list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INTEGER PRIMARY KEY, Tag varchar(50) UNIQUE)"); - list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum BLOB, TagId INTEGER, FOREIGN KEY (EntryNum)" - + " REFERENCES MsgTable(EntryNum), FOREIGN KEY (TagId) REFERENCES TagTable(TagId), UNIQUE (EntryNum, TagID))"); + list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum BLOB, TagId INTEGER," + + " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE," + + " FOREIGN KEY (TagId) REFERENCES TagTable(TagId) ON DELETE CASCADE," + + " UNIQUE (EntryNum, TagID))"); list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INTEGER, SignerId BLOB, Signature BLOB," - + " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum), UNIQUE(SignerId, EntryNum))"); + + " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE," + + " UNIQUE(SignerId, EntryNum))"); list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)"); + list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (BatchId INTEGER PRIMARY KEY, Tags BLOB)"); + + list.add("CREATE TABLE IF NOT EXISTS BatchTable (BatchId INTEGER, EntryNum INTEGER, SerialNum INTEGER, Data BLOB," + + " UNIQUE(BatchId, SerialNum)," + + " FOREIGN KEY (BatchId) REFERENCES BatchTagTable(BatchId) ON DELETE CASCADE)"); + return list; } diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/MessageStubMapper.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/MessageStubMapper.java index 1f9c459..e9174e5 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/MessageStubMapper.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/MessageStubMapper.java @@ -21,7 +21,7 @@ public class MessageStubMapper implements RowMapper { return BulletinBoardMessage.newBuilder() .setEntryNum(rs.getLong(1)) .setMsg(UnsignedBulletinBoardMessage.newBuilder() - .setData(ByteString.copyFrom(rs.getBytes(2))) + .setMsgId(ByteString.copyFrom(rs.getBytes(2))) .setTimestamp(BulletinBoardUtils.toTimestampProto(rs.getTimestamp(3))) .build()) .build(); diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java index 7389090..f9d3a94 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java @@ -10,6 +10,7 @@ import javax.ws.rs.core.StreamingOutput; import com.google.protobuf.BoolValue; import com.google.protobuf.Int32Value; +import com.google.protobuf.Int64Value; import meerkat.bulletinboard.BulletinBoardServer; import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; import meerkat.bulletinboard.sqlserver.H2QueryProvider; @@ -142,7 +143,7 @@ public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextL @Consumes(MEDIATYPE_PROTOBUF) @Produces(MEDIATYPE_PROTOBUF) @Override - public BoolValue beginBatch(BeginBatchMessage message) { + public Int64Value beginBatch(BeginBatchMessage message) { try { init(); return bulletinBoard.beginBatch(message); diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/GenericBulletinBoardServerTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/GenericBulletinBoardServerTest.java index a5997a3..eafd80c 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/GenericBulletinBoardServerTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/GenericBulletinBoardServerTest.java @@ -20,6 +20,7 @@ import java.util.*; import com.google.protobuf.BoolValue; import com.google.protobuf.ByteString; +import com.google.protobuf.Int64Value; import com.google.protobuf.Timestamp; import meerkat.comm.CommunicationException; import meerkat.comm.MessageInputStream; @@ -443,24 +444,18 @@ public class GenericBulletinBoardServerTest { } - private void postAsBatch(BulletinBoardMessage message, ByteString signerId, int batchId, int chunkSize, boolean close) throws CommunicationException { + private void postAsBatch(BulletinBoardMessage message, int chunkSize, boolean close) throws CommunicationException { List batchChunks = BulletinBoardUtils.breakToBatch(message, chunkSize); - BeginBatchMessage beginBatchMessage = BulletinBoardUtils.generateBeginBatchMessage(signerId, batchId, message); + BeginBatchMessage beginBatchMessage = BulletinBoardUtils.generateBeginBatchMessage(message); BoolValue result; // Begin batch - result = bulletinBoardServer.beginBatch(beginBatchMessage); + Int64Value batchId = bulletinBoardServer.beginBatch(beginBatchMessage); - assertThat("Was not able to open batch", result.getValue(), is(true)); - - // Attempt to open batch again - - result = bulletinBoardServer.beginBatch(beginBatchMessage); - - assertThat("Was able to open a closed batch", result.getValue(), is(false)); + assertThat("Was not able to open batch", batchId.getValue() != -1); // Post data @@ -469,8 +464,7 @@ public class GenericBulletinBoardServerTest { for (int i = 0 ; i < batchChunks.size() ; i++){ batchMessage = BatchMessage.newBuilder() - .setSignerId(signerId) - .setBatchId(batchId) + .setBatchId(batchId.getValue()) .setSerialNum(i) .setData(batchChunks.get(i)) .build(); @@ -494,45 +488,6 @@ public class GenericBulletinBoardServerTest { } - /** - * Tests that opening the same batch ID while one is open results in failure - * @throws CommunicationException - */ - public void testReopen() throws CommunicationException, SignatureException { - - // Create data - final int BATCH_ID = 100; - final int DATA_SIZE = 1; - final int CHUNK_SIZE = 1; - final int TAG_NUMBER = 1; - - Timestamp timestamp = Timestamp.newBuilder() - .setSeconds(141510) - .setNanos(48015) - .build(); - - BulletinBoardMessage batch = bulletinBoardMessageGenerator.generateRandomMessage(signers, timestamp, DATA_SIZE, TAG_NUMBER); - - // Post batch but do not close - - postAsBatch(batch, signerIDs[0], BATCH_ID, CHUNK_SIZE, false); - - // Attempt to open batch again - - BoolValue result; - - result = bulletinBoardServer.beginBatch(BulletinBoardUtils.generateBeginBatchMessage(signerIDs[0], BATCH_ID, batch)); - - assertThat("Was able to open a closed batch", result.getValue(), is(false)); - - // Close batch - - result = bulletinBoardServer.closeBatch(BulletinBoardUtils.generateCloseBatchMessage(BATCH_ID, DATA_SIZE / CHUNK_SIZE, batch)); - - assertThat("Was not able to close batch", result.getValue(), is(true)); - - } - /** * Posts a complete batch message * @throws CommunicationException @@ -554,7 +509,7 @@ public class GenericBulletinBoardServerTest { // Post batch - postAsBatch(batch, signerIDs[0], BATCH_ID, CHUNK_SIZE, true); + postAsBatch(batch, CHUNK_SIZE, true); // Update locally stored batches batches.add(batch); diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/H2BulletinBoardServerTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/H2BulletinBoardServerTest.java index 4ee2282..fa96635 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/H2BulletinBoardServerTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/H2BulletinBoardServerTest.java @@ -106,16 +106,6 @@ public class H2BulletinBoardServerTest { System.err.println("Time of operation: " + (end - start)); } - @Test - public void testBatchReopen() { - try{ - serverTest.testReopen(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - } - @Test public void testBatch() { diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java index c4bd7fa..334620c 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java @@ -110,16 +110,6 @@ public class MySQLBulletinBoardServerTest { System.err.println("Time of operation: " + (end - start)); } - @Test - public void testBatchReopen() { - try{ - serverTest.testReopen(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - } - @Test public void testBatch() { diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteBulletinBoardServerTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteBulletinBoardServerTest.java index 1d7aae0..4dcd97b 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteBulletinBoardServerTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteBulletinBoardServerTest.java @@ -60,7 +60,7 @@ public class SQLiteBulletinBoardServerTest{ System.err.println("Time of operation: " + (end - start)); } - @Test +// @Test public void bulkTest() { System.err.println("Starting bulkTest of SQLiteBulletinBoardServerTest"); long start = threadBean.getCurrentThreadCpuTime(); @@ -91,6 +91,29 @@ public class SQLiteBulletinBoardServerTest{ System.err.println("Time of operation: " + (end - start)); } +// @Test + public void testBatch() { + + final int BATCH_NUM = 20; + + try{ + for (int i = 0 ; i < BATCH_NUM ; i++) { + serverTest.testPostBatch(); + } + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + try{ + serverTest.testReadBatch(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + } + @After public void close() { System.err.println("Starting to close SQLiteBulletinBoardServerTest"); diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java index fcc6394..42af8ef 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java @@ -1,8 +1,9 @@ package meerkat.bulletinboard; import com.google.common.util.concurrent.FutureCallback; -import com.google.protobuf.ByteString; +import com.google.protobuf.Timestamp; import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Crypto.Signature; import java.util.List; @@ -21,68 +22,58 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient { public MessageID postMessage(BulletinBoardMessage msg, FutureCallback callback); /** - * Perform an end-to-end post of a signed batch message - * @param signerId is the canonical form for the ID of the sender of this batch - * @param batchId is a unique (per signer) ID for this batch - * @param completeBatch contains all the data of the batch including the meta-data and the signature + * Perform an end-to-end post of a message in batch form + * @param completeBatch contains all the data of the batch + * @param chunkSize is the maximum size of each chunk of the message in bytes * @param callback is a class containing methods to handle the result of the operation * @return a unique identifier for the batch message */ - public MessageID postBatch(byte[] signerId, int batchId, BulletinBoardMessage completeBatch, FutureCallback callback); + public MessageID postAsBatch(BulletinBoardMessage completeBatch, int chunkSize, FutureCallback callback); /** - * Perform an end-to-end post of a signed batch message - * @param signerId is the canonical form for the ID of the sender of this batch - * @param batchId is a unique (per signer) ID for this batch - * @param completeBatch contains all the data of the batch including the meta-data and the signature - * @param callback is a class containing methods to handle the result of the operation - * @return a unique identifier for the batch message + * An interface for returning an opaque identifier for a batch message + * This identifier is used to uniquely identify the batch until it is completely posted and signed + * After the batch is fully posted: it is identified by its digest (like any message) + * This can be implementation-specific (and not necessarily interchangeable between different implementations) */ - public MessageID postBatch(ByteString signerId, int batchId, BulletinBoardMessage completeBatch, FutureCallback callback); + public interface BatchIdentifier {} /** * This message informs the server about the existence of a new batch message and supplies it with the tags associated with it - * @param beginBatchMessage contains the data required to begin the batch + * @param tags contains the tags used in the batch * @param callback is a callback function class for handling results of the operation + * it receives a BatchIdentifier for use in subsequent batch post operations */ - public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback callback); + public void beginBatch(Iterable tags, FutureCallback callback); /** * This method posts batch data into an (assumed to be open) batch * It does not close the batch - * @param signerId is the canonical form for the ID of the sender of this batch - * @param batchId is a unique (per signer) ID for this batch + * @param batchIdentifier is the temporary batch identifier * @param batchChunkList is the (canonically ordered) list of data comprising the portion of the batch to be posted * @param startPosition is the location (in the batch) of the first entry in batchDataList - * (optionally used to continue interrupted post operations) - * The first position in the batch is position 0 + * (optionally used to continue interrupted post operations) + * The first position in the batch is position 0 * @param callback is a callback function class for handling results of the operation + * @throws IllegalArgumentException if the batch identifier given was of an illegal format */ - public void postBatchData(byte[] signerId, int batchId, List batchChunkList, - int startPosition, FutureCallback callback); + public void postBatchData(BatchIdentifier batchIdentifier, List batchChunkList, + int startPosition, FutureCallback callback) throws IllegalArgumentException; /** * Overloading of the postBatchData method which starts at the first position in the batch */ - public void postBatchData(byte[] signerId, int batchId, List batchChunkList, FutureCallback callback); - - /** - * Overloading of the postBatchData method which uses ByteString - */ - public void postBatchData(ByteString signerId, int batchId, List batchChunkList, - int startPosition, FutureCallback callback); - - /** - * Overloading of the postBatchData method which uses ByteString and starts at the first position in the batch - */ - public void postBatchData(ByteString signerId, int batchId, List batchChunkList, FutureCallback callback); + public void postBatchData(BatchIdentifier batchIdentifier, List batchChunkList, FutureCallback callback) + throws IllegalArgumentException; /** * Attempts to close a batch message - * @param closeBatchMessage contains the data required to close the batch + * @param batchIdentifier is the temporary batch identifier * @param callback is a callback function class for handling results of the operation + * @throws IllegalArgumentException if the batch identifier given was of an illegal format */ - public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback callback); + public void closeBatch(BatchIdentifier batchIdentifier, Timestamp timestamp, Iterable signatures, FutureCallback callback) + throws IllegalArgumentException; /** * Check how "safe" a given message is in an asynchronous manner @@ -110,6 +101,15 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient { */ public void readBatch(MessageID msgID, FutureCallback callback); + /** + * Read batch data for a specific stub message + * @param stub is a batch message stub + * @param callback is a callback class for handling the result of the operation + * @return a new BulletinBoardMessage containing both metadata from the stub and actual data from the server + * @throws IllegalArgumentException if the received message is not a stub + */ + public void readBatchData(BulletinBoardMessage stub, FutureCallback callback) throws IllegalArgumentException; + /** * Perform a Sync Query on the bulletin board diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java index 9ce3943..142bb35 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java @@ -45,14 +45,32 @@ public interface BulletinBoardClient { */ List readMessages(MessageFilterList filterList) throws CommunicationException; + /** + * Breaks up a bulletin board message into chunks and posts it as a batch message + * @param msg is the message to post + * @param chunkSize is the maximal chunk size in bytes + * @return the unique message ID + * @throws CommunicationException if operation is unsuccessful + */ + MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException; + /** * Read a given batch message from the bulletin board * @param msgID is the batch message ID to be read * @return the complete batch - * @throws CommunicationException + * @throws CommunicationException if operation is unsuccessful */ BulletinBoardMessage readBatch(MessageID msgID) throws CommunicationException; + /** + * Read batch data for a specific stub message + * @param stub is a batch message stub + * @return a new BulletinBoardMessage containing both metadata from the stub and actual data from the server + * @throws CommunicationException if operation is unsuccessful + * @throws IllegalArgumentException if the received message is not a stub + */ + BulletinBoardMessage readBatchData(BulletinBoardMessage stub) throws CommunicationException, IllegalArgumentException; + /** * Create a SyncQuery to test against that corresponds with the current server state for a specific filter list * Should only be called on instances for which the actual server contacted is known (i.e. there is only one server) diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java index 3d33704..8121b85 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java @@ -2,6 +2,7 @@ package meerkat.bulletinboard; import com.google.protobuf.BoolValue; import com.google.protobuf.Int32Value; +import com.google.protobuf.Int64Value; import meerkat.comm.CommunicationException; import meerkat.comm.MessageOutputStream; import meerkat.protobuf.BulletinBoardAPI.*; @@ -50,12 +51,10 @@ public interface BulletinBoardServer{ /** * Informs server about a new batch message * @param message contains the required data about the new batch - * @return TRUE if the batch request is accepted amd FALSE otherwise - * Specifically, if such a batch already exists and is not yet closed: the value returned will be TRUE - * However, if such a batch exists and is already closed: the value returned will be FALSE + * @return a unique batch identifier for the new batch ; -1 if batch creation was unsuccessful * @throws CommunicationException on DB connection error */ - public BoolValue beginBatch(BeginBatchMessage message) throws CommunicationException; + public Int64Value beginBatch(BeginBatchMessage message) throws CommunicationException; /** * Posts a chunk of a batch message to the bulletin board @@ -93,7 +92,7 @@ public interface BulletinBoardServer{ * @return The generated SyncQuery * @throws CommunicationException on DB connection error */ - SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException; + public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException; /** * Queries the database for sync status with respect to a given sync query diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardDigest.java b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardDigest.java index 9930ab3..fb7bd3f 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardDigest.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardDigest.java @@ -65,7 +65,7 @@ public class GenericBulletinBoardDigest implements BulletinBoardDigest { update(msg.getTimestamp()); - if (!msg.getIsStub()){ + if (msg.getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.DATA){ update(msg.getData().toByteArray()); } diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardSignature.java b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardSignature.java index e818071..aad6466 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardSignature.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardSignature.java @@ -42,7 +42,7 @@ public class GenericBulletinBoardSignature implements BulletinBoardSignature { updateContent(msg.getTimestamp()); - if (!msg.getIsStub()){ + if (msg.getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.DATA){ updateContent(msg.getData().toByteArray()); } diff --git a/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageComparator.java b/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageComparator.java index ec9b2e0..7005568 100644 --- a/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageComparator.java +++ b/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageComparator.java @@ -47,16 +47,20 @@ public class BulletinBoardMessageComparator implements Comparator Date: Sun, 26 Jun 2016 13:06:16 +0300 Subject: [PATCH 11/15] Working client-side Batch changes --- .../CachedBulletinBoardClient.java | 14 +- .../LocalBulletinBoardClient.java | 39 +-- .../SimpleBulletinBoardClient.java | 2 +- .../SimpleBulletinBoardSynchronizer.java | 79 +----- .../SingleServerBulletinBoardClient.java | 155 ++++------- .../ThreadedBulletinBoardClient.java | 7 +- .../MultiServerReadBatchDataWorker.java | 2 +- ...java => MultiServerReadMessageWorker.java} | 10 +- .../BulletinBoardSynchronizerTest.java | 63 +++-- .../GenericBulletinBoardClientTester.java | 252 ++++++++---------- .../GenericSubscriptionClientTester.java | 8 +- .../LocalBulletinBoardClientTest.java | 7 - ...dedBulletinBoardClientIntegrationTest.java | 7 - .../sqlserver/BulletinBoardSQLServer.java | 30 ++- .../AsyncBulletinBoardClient.java | 11 +- .../bulletinboard/BulletinBoardClient.java | 9 +- 16 files changed, 292 insertions(+), 403 deletions(-) rename bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/{MultiServerReadBatchWorker.java => MultiServerReadMessageWorker.java} (56%) diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java index 31521e1..6594b0c 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java @@ -3,9 +3,7 @@ package meerkat.bulletinboard; import com.google.common.util.concurrent.FutureCallback; import com.google.protobuf.Timestamp; import meerkat.comm.CommunicationException; -import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.BulletinBoardAPI.*; -import meerkat.protobuf.Comm; import meerkat.protobuf.Crypto.Signature; import meerkat.protobuf.Voting.*; @@ -267,9 +265,9 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien } @Override - public void readBatch(final MessageID msgID, final FutureCallback callback) { + public void readMessage(final MessageID msgID, final FutureCallback callback) { - localClient.readBatch(msgID, new FutureCallback() { + localClient.readMessage(msgID, new FutureCallback() { @Override public void onSuccess(BulletinBoardMessage result) { @@ -282,7 +280,7 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien // Read from local unsuccessful: try to read from remote - remoteClient.readBatch(msgID, new FutureCallback() { + remoteClient.readMessage(msgID, new FutureCallback() { @Override public void onSuccess(BulletinBoardMessage result) { @@ -398,17 +396,17 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien } @Override - public BulletinBoardMessage readBatch(MessageID msgID) throws CommunicationException { + public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException { BulletinBoardMessage result = null; try { - result = localClient.readBatch(msgID); + result = localClient.readMessage(msgID); } catch (CommunicationException e) { //TODO: log } if (result == null){ - result = remoteClient.readBatch(msgID); + result = remoteClient.readMessage(msgID); if (result != null){ localClient.postMessage(result); diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java index f09b2d3..2b904ed 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java @@ -183,6 +183,8 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo } + batchId.setLength(i); + return true; } @@ -244,6 +246,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder() .setBatchId(identifier.getBatchId().getValue()) + .setBatchLength(identifier.getLength()) .setTimestamp(timestamp) .addAllSig(signatures) .build(); @@ -438,7 +441,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo @Override public BulletinBoardMessage call() throws Exception { - // Read message stub + // Read message (mat be a stub) MessageFilterList filterList = MessageFilterList.newBuilder() .addFilter(MessageFilter.newBuilder() @@ -451,19 +454,25 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo List bulletinBoardMessages = messageReader.call(); if (bulletinBoardMessages.size() <= 0) { - throw new NotFoundException("Batch does not exist"); + throw new NotFoundException("Message does not exist"); } - BulletinBoardMessage stub = bulletinBoardMessages.get(0); + BulletinBoardMessage msg = bulletinBoardMessages.get(0); - // Read data + if (msg.getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { - BatchDataReader batchDataReader = new BatchDataReader(msgID); - List batchChunkList = batchDataReader.call(); + // Read data - // Combine and return + BatchDataReader batchDataReader = new BatchDataReader(msgID); + List batchChunkList = batchDataReader.call(); - return BulletinBoardUtils.gatherBatch(stub, batchChunkList); + // Combine and return + + return BulletinBoardUtils.gatherBatch(msg, batchChunkList); + + } else { + return msg; + } } @@ -493,7 +502,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo } @Override - public void readBatch(MessageID msgID, FutureCallback callback) { + public void readMessage(MessageID msgID, FutureCallback callback) { Futures.addCallback(executorService.submit(new CompleteBatchReader(msgID)), callback); } @@ -591,7 +600,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo } @Override - public BulletinBoardMessage readBatch(MessageID msgID) throws CommunicationException { + public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException { MessageFilterList filterList = MessageFilterList.newBuilder() .addFilter(MessageFilter.newBuilder() @@ -617,20 +626,14 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); } - MessageID msgID = MessageID.newBuilder().setID(stub.getMsg().getMsgId()).build(); - - BatchDataReader batchDataReader = new BatchDataReader(msgID); - - List batchChunkList = null; + BatchDataCombiner combiner = new BatchDataCombiner(stub); try { - batchChunkList = batchDataReader.call(); + return combiner.call(); } catch (Exception e) { throw new CommunicationException(e.getCause() + " " + e.getMessage()); } - return BulletinBoardUtils.gatherBatch(stub, batchChunkList); - } @Override diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java index 6250784..521815d 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java @@ -224,7 +224,7 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{ } @Override - public BulletinBoardMessage readBatch(MessageID msgID) throws CommunicationException { + public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException { MessageFilterList filterList = MessageFilterList.newBuilder() .addFilter(MessageFilter.newBuilder() diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java index e8abf6b..5cdf73b 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java @@ -36,75 +36,6 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize private Semaphore semaphore; - /** - * This class is a callback that deletes a message if it has been successfully posted - * It also calls a stored callback - */ - private class MessageDeleteCallback implements FutureCallback { - - private final long entryNum; - private final FutureCallback callback; - - public MessageDeleteCallback(long entryNum, FutureCallback callback) { - this.entryNum = entryNum; - this.callback = callback; - } - - @Override - public void onSuccess(Boolean result) { - // Success: delete from database - localClient.deleteMessage(entryNum, null); - callback.onSuccess(null); - } - - @Override - public void onFailure(Throwable t) { - callback.onFailure(t); - } - - } - - /** - * This class aggregates the results from all of the post operations - * If any post has failed: it changes the sync status to SERVER_ERROR - * It also notifies the main sync loop when all uploads are finished - */ - private class SyncStatusUpdateCallback implements FutureCallback { - - private int count; - private boolean errorEncountered; - - public SyncStatusUpdateCallback(int count) { - this.count = count; - this.errorEncountered = false; - } - - private void handleStatusUpdate() { - count--; - if (count <= 0) { - - if (errorEncountered) - updateSyncStatus(SyncStatus.SERVER_ERROR); - - // Upload is done: wake up the synchronizer loop - semaphore.release(); - - } - } - - @Override - public void onSuccess(Void result) { - handleStatusUpdate(); - } - - @Override - public void onFailure(Throwable t) { - errorEncountered = true; - handleStatusUpdate(); - } - - } - private class SyncCallback implements FutureCallback> { @Override @@ -122,8 +53,6 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize // Handle upload and status change - SyncStatusUpdateCallback syncStatusUpdateCallback = new SyncStatusUpdateCallback(result.size()); - SyncStatus newStatus = SyncStatus.PENDING; if (result.size() == 0) { @@ -143,13 +72,17 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize BulletinBoardMessage completeMsg = localClient.readBatchData(message); - remoteClient.postMessage(completeMsg, new MessageDeleteCallback(message.getEntryNum(), syncStatusUpdateCallback)); + remoteClient.postMessage(completeMsg); + + localClient.deleteMessage(completeMsg.getEntryNum()); } else { // This is a regular message: post it - remoteClient.postMessage(message, new MessageDeleteCallback(message.getEntryNum(), syncStatusUpdateCallback)); + remoteClient.postMessage(message); + + localClient.deleteMessage(message.getEntryNum()); } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java index b985a8a..86ede70 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java @@ -7,6 +7,8 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.Int64Value; import com.google.protobuf.Timestamp; import meerkat.bulletinboard.workers.singleserver.*; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.protobuf.Crypto; import meerkat.protobuf.Voting.BulletinBoardClientParams; @@ -166,91 +168,74 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i } } + private class ReadBatchCallback implements FutureCallback> { + + private final BulletinBoardMessage stub; + private final FutureCallback callback; + + public ReadBatchCallback(BulletinBoardMessage stub, FutureCallback callback) { + this.stub = stub; + this.callback = callback; + } + + @Override + public void onSuccess(List result) { + callback.onSuccess(BulletinBoardUtils.gatherBatch(stub, result)); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + + } + /** - * This callback ties together the different parts of a CompleteBatch as they arrive from the server - * It assembles a CompleteBatch from the parts and sends it to the user if all parts arrived - * If any part fails to arrive: it invokes the onFailure method + * This callback receives a message which may be a stub + * If the message is not a stub: it returns it as is to a callback function + * If it is a stub: it schedules a read of the batch data which will return a complete message to the callback function */ - class CompleteBatchReadCallback { + class CompleteMessageReadCallback implements FutureCallback>{ private final FutureCallback callback; - private List batchChunkList; - private BulletinBoardMessage stub; - - private AtomicInteger remainingQueries; - private AtomicBoolean failed; - - public CompleteBatchReadCallback(FutureCallback callback) { + public CompleteMessageReadCallback(FutureCallback callback) { this.callback = callback; - remainingQueries = new AtomicInteger(2); - failed = new AtomicBoolean(false); - } - protected void combineAndReturn() { + @Override + public void onSuccess(List result) { + if (result.size() <= 0) { + onFailure(new CommunicationException("Could not find required message on the server.")); + } else { - if (remainingQueries.decrementAndGet() == 0){ + BulletinBoardMessage msg = result.get(0); - if (callback != null) - callback.onSuccess(BulletinBoardUtils.gatherBatch(stub, batchChunkList)); - } + if (msg.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { + callback.onSuccess(msg); + } else { - } + // Create job with MAX retries for retrieval of the Batch Data List - protected void fail(Throwable t) { - if (failed.compareAndSet(false, true)) { - if (callback != null) - callback.onFailure(t); + BatchQuery batchQuery = BatchQuery.newBuilder() + .setMsgID(MessageID.newBuilder() + .setID(msg.getMsg().getMsgId()) + .build()) + .build(); + + SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(meerkatDBs.get(0), batchQuery, MAX_RETRIES); + + scheduleWorker(batchWorker, new ReadBatchCallback(msg, callback)); + + } } } - /** - * @return a FutureCallback for the Batch Data List that ties to this object - */ - public FutureCallback> asBatchDataListFutureCallback() { - return new FutureCallback>() { - - @Override - public void onSuccess(List result) { - batchChunkList = result; - - combineAndReturn(); - } - - @Override - public void onFailure(Throwable t) { - fail(t); - } - - }; - } - - /** - * @return a FutureCallback for the Bulletin Board Message that ties to this object - */ - public FutureCallback> asBulletinBoardMessageListFutureCallback() { - return new FutureCallback>() { - - @Override - public void onSuccess(List result) { - if (result.size() < 1){ - onFailure(new IllegalArgumentException("Server returned empty message list")); - return; - } - - stub = result.get(0); - - combineAndReturn(); - } - - @Override - public void onFailure(Throwable t) { - fail(t); - } - }; + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); } } @@ -574,9 +559,9 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i } @Override - public void readBatch(MessageID msgID, FutureCallback callback) { + public void readMessage(MessageID msgID, FutureCallback callback) { - // Create job with MAX retries for retrieval of the Bulletin Board Message that defines the batch + // Create job with MAX retries for retrieval of the Bulletin Board Message (which may be a stub) MessageFilterList filterList = MessageFilterList.newBuilder() .addFilter(MessageFilter.newBuilder() @@ -592,39 +577,11 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i SingleServerReadMessagesWorker messageWorker = new SingleServerReadMessagesWorker(meerkatDBs.get(0), filterList, MAX_RETRIES); - // Create job with MAX retries for retrieval of the Batch Data List - SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(meerkatDBs.get(0), batchQuery, MAX_RETRIES); - - // Create callback that will combine the two worker products - CompleteBatchReadCallback completeBatchReadCallback = new CompleteBatchReadCallback(callback); - // Submit jobs with wrapped callbacks - scheduleWorker(messageWorker, new RetryCallback<>(messageWorker, completeBatchReadCallback.asBulletinBoardMessageListFutureCallback())); - scheduleWorker(batchWorker, new RetryCallback<>(batchWorker, completeBatchReadCallback.asBatchDataListFutureCallback())); + scheduleWorker(messageWorker, new RetryCallback<>(messageWorker, new CompleteMessageReadCallback(callback))); } - private class ReadBatchCallback implements FutureCallback> { - - private final BulletinBoardMessage stub; - private final FutureCallback callback; - - public ReadBatchCallback(BulletinBoardMessage stub, FutureCallback callback) { - this.stub = stub; - this.callback = callback; - } - - @Override - public void onSuccess(List result) { - callback.onSuccess(BulletinBoardUtils.gatherBatch(stub, result)); - } - - @Override - public void onFailure(Throwable t) { - - } - } - @Override public void readBatchData(BulletinBoardMessage stub, FutureCallback callback) throws IllegalArgumentException{ diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java index 7f8232f..5c1cab4 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java @@ -4,7 +4,6 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.protobuf.Timestamp; import meerkat.bulletinboard.workers.multiserver.*; -import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.protobuf.Crypto.Signature; import meerkat.protobuf.Voting.*; @@ -208,11 +207,11 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple } @Override - public void readBatch(MessageID msgID, FutureCallback callback) { + public void readMessage(MessageID msgID, FutureCallback callback) { //Create job - MultiServerReadBatchWorker worker = - new MultiServerReadBatchWorker(clients, minAbsoluteRedundancy, msgID, READ_MESSAGES_RETRY_NUM, callback); + MultiServerReadMessageWorker worker = + new MultiServerReadMessageWorker(clients, minAbsoluteRedundancy, msgID, READ_MESSAGES_RETRY_NUM, callback); // Submit job executorService.submit(worker); diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java index 959c60f..ab5fd40 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java @@ -22,7 +22,7 @@ public class MultiServerReadBatchDataWorker extends MultiServerGenericReadWorker @Override protected void doRead(MessageID payload, SingleServerBulletinBoardClient client) { - client.readBatch(payload, this); + client.readMessage(payload, this); } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadMessageWorker.java similarity index 56% rename from bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchWorker.java rename to bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadMessageWorker.java index 59b4ce5..f84d67e 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadMessageWorker.java @@ -11,11 +11,11 @@ import java.util.List; /** * Created by Arbel Deutsch Peled on 27-Dec-15. */ -public class MultiServerReadBatchWorker extends MultiServerGenericReadWorker { +public class MultiServerReadMessageWorker extends MultiServerGenericReadWorker { - public MultiServerReadBatchWorker(List clients, - int minServers, MessageID payload, int maxRetry, - FutureCallback futureCallback) { + public MultiServerReadMessageWorker(List clients, + int minServers, MessageID payload, int maxRetry, + FutureCallback futureCallback) { super(clients, minServers, payload, maxRetry, futureCallback); @@ -23,7 +23,7 @@ public class MultiServerReadBatchWorker extends MultiServerGenericReadWorker 0) { - for (Throwable t : thrown) - System.err.println(t.getMessage()); - assertThat("Exception thrown by Synchronizer: " + thrown.get(0).getMessage(), false); - } - } @After public void close() { + if (thrown.size() > 0) { + for (Throwable t : thrown) { + System.err.println(t.getMessage()); + } + assertThat("Exception thrown by Synchronizer: " + thrown.get(0).getMessage(), false); + } + synchronizer.stop(); localClient.close(); remoteClient.close(); diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericBulletinBoardClientTester.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericBulletinBoardClientTester.java index fa708d5..5e60957 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericBulletinBoardClientTester.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericBulletinBoardClientTester.java @@ -5,9 +5,13 @@ import com.google.protobuf.ByteString; import com.google.protobuf.Timestamp; import meerkat.comm.CommunicationException; import meerkat.crypto.concrete.ECDSASignature; +import meerkat.crypto.concrete.SHA256Digest; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.protobuf.Crypto; import meerkat.util.BulletinBoardMessageComparator; +import meerkat.util.BulletinBoardMessageGenerator; +import meerkat.util.BulletinBoardUtils; +import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; import java.io.IOException; import java.io.InputStream; @@ -27,7 +31,7 @@ public class GenericBulletinBoardClientTester { // Signature resources - private GenericBatchDigitalSignature signers[]; + private BulletinBoardSignature signers[]; private ByteString[] signerIDs; private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12"; @@ -48,13 +52,15 @@ public class GenericBulletinBoardClientTester { private RedundancyCallback redundancyCallback; private ReadCallback readCallback; - private ReadBatchCallback readBatchCallback; // Sync and misc private Semaphore jobSemaphore; private Vector thrown; private Random random; + private BulletinBoardMessageGenerator generator; + + private BulletinBoardDigest digest; // Constructor @@ -62,10 +68,10 @@ public class GenericBulletinBoardClientTester { this.bulletinBoardClient = bulletinBoardClient; - signers = new GenericBatchDigitalSignature[2]; + signers = new GenericBulletinBoardSignature[2]; signerIDs = new ByteString[signers.length]; - signers[0] = new GenericBatchDigitalSignature(new ECDSASignature()); - signers[1] = new GenericBatchDigitalSignature(new ECDSASignature()); + signers[0] = new GenericBulletinBoardSignature(new ECDSASignature()); + signers[1] = new GenericBulletinBoardSignature(new ECDSASignature()); InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE); char[] password = KEYFILE_PASSWORD1.toCharArray(); @@ -107,6 +113,10 @@ public class GenericBulletinBoardClientTester { fail("Couldn't find signing key " + e.getMessage()); } + this.random = new Random(0); + this.generator = new BulletinBoardMessageGenerator(random); + this.digest = new GenericBulletinBoardDigest(new SHA256Digest()); + } // Callback definitions @@ -137,16 +147,21 @@ public class GenericBulletinBoardClientTester { @Override public void onSuccess(Boolean msg) { + System.err.println("Post operation completed"); - jobSemaphore.release(); - //TODO: Change Assert mechanism to exception one + if (isAssert) { - if (assertValue) { - assertThat("Post operation failed", msg, is(Boolean.TRUE)); + if (assertValue && !msg) { + genericHandleFailure(new AssertionError("Post operation failed")); + } else if (!assertValue && msg){ + genericHandleFailure(new AssertionError("Post operation succeeded unexpectedly")); } else { - assertThat("Post operation succeeded unexpectedly", msg, is(Boolean.FALSE)); + jobSemaphore.release(); } + } else { + jobSemaphore.release(); } + } @Override @@ -209,21 +224,24 @@ public class GenericBulletinBoardClientTester { } } - private class ReadBatchCallback implements FutureCallback { + private class ReadBatchCallback implements FutureCallback{ - private CompleteBatch expectedBatch; + private BulletinBoardMessage expectedMsg; - public ReadBatchCallback(CompleteBatch expectedBatch) { - this.expectedBatch = expectedBatch; + public ReadBatchCallback(BulletinBoardMessage expectedMsg) { + this.expectedMsg = expectedMsg; } @Override - public void onSuccess(CompleteBatch batch) { + public void onSuccess(BulletinBoardMessage msg) { - System.err.println(batch); - jobSemaphore.release(); + BulletinBoardMessageComparator msgComparator = new BulletinBoardMessageComparator(); - assertThat("Batch returned is incorrect", batch, is(equalTo(expectedBatch))); + if (msgComparator.compare(msg, expectedMsg) != 0) { + genericHandleFailure(new AssertionError("Batch read returned different message.\nExpected:" + expectedMsg + "\nRecieved:" + msg + "\n")); + } else { + jobSemaphore.release(); + } } @@ -233,59 +251,6 @@ public class GenericBulletinBoardClientTester { } } - // Randomness generators - - private byte randomByte(){ - return (byte) random.nextInt(); - } - - private byte[] randomByteArray(int length) { - - byte[] randomBytes = new byte[length]; - - for (int i = 0; i < length ; i++){ - randomBytes[i] = randomByte(); - } - - return randomBytes; - - } - - private CompleteBatch createRandomBatch(int signer, int batchId, int length) throws SignatureException { - - CompleteBatch completeBatch = new CompleteBatch(); - - // Create data - - completeBatch.setBeginBatchMessage(BeginBatchMessage.newBuilder() - .setSignerId(signerIDs[signer]) - .setBatchId(batchId) - .addTag("Test") - .build()); - - for (int i = 0 ; i < length ; i++){ - - BatchChunk batchChunk = BatchChunk.newBuilder() - .setData(ByteString.copyFrom(randomByteArray(i))) - .build(); - - completeBatch.appendBatchData(batchChunk); - - } - - completeBatch.setTimestamp(Timestamp.newBuilder() - .setSeconds(Math.abs(90)) - .setNanos(50) - .build()); - - signers[signer].updateContent(completeBatch); - - completeBatch.setSignature(signers[signer].sign()); - - return completeBatch; - - } - // Test methods /** @@ -310,7 +275,13 @@ public class GenericBulletinBoardClientTester { public void close() { if (thrown.size() > 0) { + + for (Throwable t : thrown){ + System.err.println(t.getMessage()); + } + assert false; + } } @@ -394,59 +365,54 @@ public class GenericBulletinBoardClientTester { /** * Tests posting a batch by parts - * Also tests not being able to post to a closed batch * @throws CommunicationException, SignatureException, InterruptedException */ public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException { - final int SIGNER = 1; - final int BATCH_ID = 100; final int BATCH_LENGTH = 100; + final int CHUNK_SIZE = 10; + final int TAG_NUM = 10; - CompleteBatch completeBatch = createRandomBatch(SIGNER, BATCH_ID, BATCH_LENGTH); + final BulletinBoardMessage msg = generator.generateRandomMessage(signers, BATCH_LENGTH, TAG_NUM); // Begin batch - bulletinBoardClient.beginBatch(completeBatch.getBeginBatchMessage(), postCallback); + bulletinBoardClient.beginBatch(msg.getMsg().getTagList(), new FutureCallback() { + + @Override + public void onSuccess(final BatchIdentifier identifier) { + + bulletinBoardClient.postBatchData(identifier, BulletinBoardUtils.breakToBatch(msg, CHUNK_SIZE), new FutureCallback() { + + @Override + public void onSuccess(Boolean result) { + + bulletinBoardClient.closeBatch(identifier, msg.getMsg().getTimestamp(), msg.getSigList(), postCallback); + + } + + @Override + public void onFailure(Throwable t) { + genericHandleFailure(t); + } + + }); + + } + + @Override + public void onFailure(Throwable t) { + genericHandleFailure(t); + } + + }); jobSemaphore.acquire(); - // Post data + digest.reset(); + digest.update(msg); - bulletinBoardClient.postBatchData(signerIDs[SIGNER], BATCH_ID, completeBatch.getBatchDataList(), postCallback); - - jobSemaphore.acquire(); - - // Close batch - - CloseBatchMessage closeBatchMessage = completeBatch.getCloseBatchMessage(); - - bulletinBoardClient.closeBatch(closeBatchMessage, postCallback); - - jobSemaphore.acquire(); - - // Attempt to open batch again - - bulletinBoardClient.beginBatch(completeBatch.getBeginBatchMessage(), failPostCallback); - - // Attempt to add batch data - - bulletinBoardClient.postBatchData(signerIDs[SIGNER], BATCH_ID, completeBatch.getBatchDataList(), failPostCallback); - - jobSemaphore.acquire(2); - - // Read batch data - - BatchSpecificationMessage batchSpecificationMessage = - BatchSpecificationMessage.newBuilder() - .setSignerId(signerIDs[SIGNER]) - .setBatchId(BATCH_ID) - .setStartPosition(0) - .build(); - - readBatchCallback = new ReadBatchCallback(completeBatch); - - bulletinBoardClient.readBatch(batchSpecificationMessage, readBatchCallback); + bulletinBoardClient.readMessage(digest.digestAsMessageID(), new ReadBatchCallback(msg)); jobSemaphore.acquire(); @@ -454,62 +420,56 @@ public class GenericBulletinBoardClientTester { /** * Posts a complete batch message - * Checks reading of the message + * Checks reading of the message in two parts * @throws CommunicationException, SignatureException, InterruptedException */ public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException { - final int SIGNER = 0; - final int BATCH_ID = 101; - final int BATCH_LENGTH = 50; + final int BATCH_LENGTH = 100; + final int CHUNK_SIZE = 99; + final int TAG_NUM = 8; + + final BulletinBoardMessage msg = generator.generateRandomMessage(signers, BATCH_LENGTH, TAG_NUM); // Post batch - CompleteBatch completeBatch = createRandomBatch(SIGNER, BATCH_ID, BATCH_LENGTH); - - bulletinBoardClient.postBatch(completeBatch,postCallback); + MessageID msgID = bulletinBoardClient.postAsBatch(msg, CHUNK_SIZE, postCallback); jobSemaphore.acquire(); // Read batch - BatchSpecificationMessage batchSpecificationMessage = - BatchSpecificationMessage.newBuilder() - .setSignerId(signerIDs[SIGNER]) - .setBatchId(BATCH_ID) - .setStartPosition(0) - .build(); + MessageFilterList filterList = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.MSG_ID) + .setId(msgID.getID()) + .build()) + .build(); - readBatchCallback = new ReadBatchCallback(completeBatch); + bulletinBoardClient.readMessages(filterList, new FutureCallback>() { - bulletinBoardClient.readBatch(batchSpecificationMessage, readBatchCallback); + @Override + public void onSuccess(List msgList) { - jobSemaphore.acquire(); + if (msgList.size() != 1) { - } + genericHandleFailure(new AssertionError("Wrong number of stubs returned. Expected: 1; Found: " + msgList.size())); - /** - * Tests that an unopened batch cannot be closed - * @throws CommunicationException, InterruptedException - */ - public void testInvalidBatchClose() throws CommunicationException, InterruptedException { + } else { - final int NON_EXISTENT_BATCH_ID = 999; + BulletinBoardMessage retrievedMsg = msgList.get(0); + bulletinBoardClient.readBatchData(retrievedMsg, new ReadBatchCallback(msg)); - CloseBatchMessage closeBatchMessage = - CloseBatchMessage.newBuilder() - .setBatchId(NON_EXISTENT_BATCH_ID) - .setBatchLength(1) - .setSig(Crypto.Signature.getDefaultInstance()) - .setTimestamp(Timestamp.newBuilder() - .setSeconds(9) - .setNanos(12) - .build()) - .build(); + } - // Try to stop the (unopened) batch; + } - bulletinBoardClient.closeBatch(closeBatchMessage, failPostCallback); + @Override + public void onFailure(Throwable t) { + genericHandleFailure(t); + } + + }); jobSemaphore.acquire(); diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java index 52da797..ea74885 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java @@ -23,7 +23,7 @@ import static org.junit.Assert.fail; */ public class GenericSubscriptionClientTester { - private GenericBatchDigitalSignature signers[]; + private BulletinBoardSignature signers[]; private ByteString[] signerIDs; private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12"; @@ -47,10 +47,10 @@ public class GenericSubscriptionClientTester { this.bulletinBoardClient = bulletinBoardClient; - signers = new GenericBatchDigitalSignature[2]; + signers = new BulletinBoardSignature[2]; signerIDs = new ByteString[signers.length]; - signers[0] = new GenericBatchDigitalSignature(new ECDSASignature()); - signers[1] = new GenericBatchDigitalSignature(new ECDSASignature()); + signers[0] = new GenericBulletinBoardSignature(new ECDSASignature()); + signers[1] = new GenericBulletinBoardSignature(new ECDSASignature()); InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE); char[] password = KEYFILE_PASSWORD1.toCharArray(); diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java index 0ab5c67..1804c77 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java @@ -100,13 +100,6 @@ public class LocalBulletinBoardClientTest { } - @Test - public void testInvalidBatchClose() throws CommunicationException, InterruptedException { - - clientTest.testInvalidBatchClose(); - - } - @Test public void testSubscription() throws SignatureException, CommunicationException { subscriptionTester.init(); diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java index b69cf72..1a95e95 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java @@ -85,11 +85,4 @@ public class ThreadedBulletinBoardClientIntegrationTest { } - @Test - public void testInvalidBatchClose() throws CommunicationException, InterruptedException { - - clientTest.testInvalidBatchClose(); - - } - } diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java index dcea3e7..e9c910a 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java @@ -19,6 +19,7 @@ import meerkat.crypto.DigitalSignature; import meerkat.crypto.concrete.SHA256Digest; import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Comm; import meerkat.protobuf.Crypto.Signature; import meerkat.protobuf.Crypto.SignatureVerificationKey; @@ -546,9 +547,17 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ } + private void checkConnection() throws CommunicationException { + if (jdbcTemplate == null) { + throw new CommunicationException("DB connection not initialized"); + } + } + @Override public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException { + checkConnection(); + // Perform a post, calculate the message ID and check the signature for authenticity if (postMessage(msg, null) != -1){ return BoolValue.newBuilder().setValue(true).build(); // Message was posted @@ -561,6 +570,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ @Override public BoolValue deleteMessage(MessageID msgID) throws CommunicationException { + checkConnection(); + String sql = sqlQueryProvider.getSQLString(QueryType.DELETE_MSG_BY_ID); Map namedParameters = new HashMap(); @@ -577,6 +588,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ @Override public BoolValue deleteMessage(long entryNum) throws CommunicationException { + checkConnection(); + String sql = sqlQueryProvider.getSQLString(QueryType.DELETE_MSG_BY_ENTRY); Map namedParameters = new HashMap(); @@ -702,6 +715,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ @Override public void readMessages(MessageFilterList filterList, MessageOutputStream out) throws CommunicationException { + checkConnection(); + BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder(); // SQL length is roughly 50 characters per filter + 50 for the query itself @@ -725,6 +740,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ @Override public Int32Value getMessageCount(MessageFilterList filterList) throws CommunicationException { + checkConnection(); + BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder(); // SQL length is roughly 50 characters per filter + 50 for the query itself @@ -793,6 +810,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ @Override public Int64Value beginBatch(BeginBatchMessage message) throws CommunicationException { + checkConnection(); + // Store tags String sql = sqlQueryProvider.getSQLString(QueryType.STORE_BATCH_TAGS); MapSqlParameterSource namedParameters = new MapSqlParameterSource(); @@ -814,6 +833,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ @Override public BoolValue postBatchMessage(BatchMessage batchMessage) throws CommunicationException{ + checkConnection(); + // Make sure batch is open if (!isBatchOpen(batchMessage.getBatchId())) { return BoolValue.newBuilder().setValue(false).build(); @@ -837,6 +858,7 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ @Override public BoolValue closeBatch(CloseBatchMessage message) throws CommunicationException { + checkConnection(); // Check batch size @@ -916,6 +938,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ @Override public void readBatch(BatchQuery batchQuery, MessageOutputStream out) throws CommunicationException, IllegalArgumentException{ + checkConnection(); + // Check that batch is closed if (!isBatchClosed(batchQuery.getMsgID())) { throw new IllegalArgumentException("No such batch"); @@ -950,7 +974,9 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ } @Override - public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) { + public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException{ + + checkConnection(); if (generateSyncQueryParams == null || !generateSyncQueryParams.hasFilterList() @@ -1029,6 +1055,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ @Override public SyncQueryResponse querySync(SyncQuery syncQuery) throws CommunicationException { + checkConnection(); + if (syncQuery == null){ return SyncQueryResponse.newBuilder() .setLastEntryNum(-1) diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java index 42af8ef..ab71a76 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java @@ -87,19 +87,20 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient { * Read all messages posted matching the given filter in an asynchronous manner * 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. + * are guaranteed to be included * Also: batch messages are returned as stubs. - * @param filterList return only messages that match the filters (null means no filtering). + * @param filterList return only messages that match the filters (null means no filtering) * @param callback is a callback function class for handling results of the operation */ public void readMessages(MessageFilterList filterList, FutureCallback> callback); /** - * Read a given batch message from the bulletin board - * @param msgID is the batch message ID to be read + * Read a given message from the bulletin board + * If the message is a batch: returns a complete message containing the batch data as well as the metadata + * @param msgID is the ID of the message to be read * @param callback is a callback class for handling the result of the operation */ - public void readBatch(MessageID msgID, FutureCallback callback); + public void readMessage(MessageID msgID, FutureCallback callback); /** * Read batch data for a specific stub message diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java index 142bb35..7f29608 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java @@ -55,12 +55,13 @@ public interface BulletinBoardClient { MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException; /** - * Read a given batch message from the bulletin board - * @param msgID is the batch message ID to be read - * @return the complete batch + * Read a given message from the bulletin board + * If the message is a batch: returns a complete message containing the batch data as well as the metadata + * @param msgID is the ID of the message to be read + * @return the complete message * @throws CommunicationException if operation is unsuccessful */ - BulletinBoardMessage readBatch(MessageID msgID) throws CommunicationException; + BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException; /** * Read batch data for a specific stub message From cc2888483de408b97b79e0b67bf5ec3ffe07306e Mon Sep 17 00:00:00 2001 From: Arbel Deutsch Peled Date: Sun, 26 Jun 2016 14:32:30 +0300 Subject: [PATCH 12/15] Threaded Client integration tests passing --- .../ThreadedBulletinBoardClient.java | 11 +++- .../MultiServerBeginBatchWorker.java | 2 +- .../MultiServerCloseBatchWorker.java | 60 +++++++++++++++++-- .../MultiServerPostBatchDataWorker.java | 2 +- .../SingleServerBeginBatchWorker.java | 7 ++- .../GenericBulletinBoardClientTester.java | 28 +++++++-- .../LocalBulletinBoardClientTest.java | 4 +- ...dedBulletinBoardClientIntegrationTest.java | 4 +- 8 files changed, 98 insertions(+), 20 deletions(-) diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java index 5c1cab4..b549a2e 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java @@ -160,11 +160,18 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple } @Override - public void closeBatch(BatchIdentifier payload, Timestamp timestamp, Iterable signatures, FutureCallback callback) { + public void closeBatch(BatchIdentifier payload, Timestamp timestamp, Iterable signatures, FutureCallback callback) + throws IllegalArgumentException{ + + if (!(payload instanceof MultiServerBatchIdentifier)) { + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + MultiServerBatchIdentifier identifier = (MultiServerBatchIdentifier) payload; // Create job MultiServerCloseBatchWorker worker = - new MultiServerCloseBatchWorker(clients, minAbsoluteRedundancy, payload, timestamp, signatures, POST_MESSAGE_RETRY_NUM, callback); + new MultiServerCloseBatchWorker(clients, minAbsoluteRedundancy, identifier, timestamp, signatures, POST_MESSAGE_RETRY_NUM, callback); // Submit job executorService.submit(worker); diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerBeginBatchWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerBeginBatchWorker.java index 793fc6e..7d64946 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerBeginBatchWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerBeginBatchWorker.java @@ -46,7 +46,7 @@ public class MultiServerBeginBatchWorker extends MultiServerWorker { +public class MultiServerCloseBatchWorker extends MultiServerWorker { private final Timestamp timestamp; - private final Iterable signatures; + private final Iterable signatures; - public MultiServerCloseBatchWorker(List clients, int minServers, - BatchIdentifier payload, Timestamp timestamp, Iterable signatures, + public MultiServerCloseBatchWorker(List clients, + int minServers, MultiServerBatchIdentifier payload, Timestamp timestamp, Iterable signatures, int maxRetry, FutureCallback futureCallback) { super(clients, minServers, payload, maxRetry, futureCallback); @@ -28,8 +34,50 @@ public class MultiServerCloseBatchWorker extends MultiServerGenericPostWorker identifierIterator = payload.getIdentifiers().iterator(); + + // Iterate through client + + for (SingleServerBulletinBoardClient client : clients) { + + if (identifierIterator.hasNext()) { + + // Fetch the batch identifier supplied by the specific client (may be null if batch open failed on client + + BatchIdentifier identifier = identifierIterator.next(); + + if (identifier != null) { + + // Post the data with the matching identifier to the client + client.closeBatch(identifier, timestamp, signatures, this); + + } else { + + // Count servers with no batch identifier as failed + maxFailedServers.decrementAndGet(); + + } + + } + + } + + } + + @Override + public void onSuccess(Boolean result) { + if (minServers.decrementAndGet() <= 0){ + succeed(result); + } + } + + @Override + public void onFailure(Throwable t) { + if (maxFailedServers.decrementAndGet() <= 0){ + fail(t); + } } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchDataWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchDataWorker.java index 052b625..8ef5931 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchDataWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchDataWorker.java @@ -40,7 +40,7 @@ public class MultiServerPostBatchDataWorker extends MultiServerWorker() { + @Override + public void onSuccess(Boolean result) { + jobSemaphore.release(); + } + + @Override + public void onFailure(Throwable t) { + genericHandleFailure(t); + } + }); } @@ -429,7 +444,12 @@ public class GenericBulletinBoardClientTester { final int CHUNK_SIZE = 99; final int TAG_NUM = 8; - final BulletinBoardMessage msg = generator.generateRandomMessage(signers, BATCH_LENGTH, TAG_NUM); + final Timestamp timestamp = Timestamp.newBuilder() + .setSeconds(7776151) + .setNanos(252616) + .build(); + + final BulletinBoardMessage msg = generator.generateRandomMessage(signers, timestamp, BATCH_LENGTH, TAG_NUM); // Post batch diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java index 1804c77..56392d7 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java @@ -81,9 +81,9 @@ public class LocalBulletinBoardClientTest { } @Test - public void postTest() { + public void testPost() { - clientTest.postTest(); + clientTest.testPost(); } diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java index 1a95e95..dd47354 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java @@ -66,9 +66,9 @@ public class ThreadedBulletinBoardClientIntegrationTest { } @Test - public void postTest() { + public void testPost() { - clientTest.postTest(); + clientTest.testPost(); } From 53d609bfee483c348f6d35396c817fa6aecc95d1 Mon Sep 17 00:00:00 2001 From: "arbel.peled" Date: Tue, 28 Jun 2016 08:19:29 +0300 Subject: [PATCH 13/15] Removed database address from Bulletin Board Server init method (the address is given to the Query Provider). Added integration tests for Single Server Bulletin Board Client. Fixed subscription logic in the Single Server Bulletin Board Client. Decoupled Single Server- and Simple- Bulletin Board Clients (Simple-... is now obsolete). Fixed some bugs. Threaded Bulletin Board Client now has some errors in integration. --- .../CachedBulletinBoardClient.java | 45 ++- .../SimpleBulletinBoardClient.java | 13 +- .../SingleServerBulletinBoardClient.java | 284 ++++++++++++++++-- .../ThreadedBulletinBoardSubscriber.java | 2 - .../SingleServerGenerateSyncQueryWorker.java | 55 ++++ .../BulletinBoardSynchronizerTest.java | 4 +- .../CachedBulletinBoardClientTest.java | 111 +++++++ .../GenericSubscriptionClientTester.java | 8 +- .../LocalBulletinBoardClientTest.java | 27 +- ...verBulletinBoardClientIntegrationTest.java | 104 +++++++ .../sqlserver/BulletinBoardSQLServer.java | 2 +- .../webapp/BulletinBoardWebApp.java | 10 +- .../H2BulletinBoardServerTest.java | 2 +- .../MySQLBulletinBoardServerTest.java | 2 +- .../SQLiteBulletinBoardServerTest.java | 2 +- .../bulletinboard/BulletinBoardClient.java | 3 +- .../bulletinboard/BulletinBoardServer.java | 2 +- 17 files changed, 575 insertions(+), 101 deletions(-) create mode 100644 bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGenerateSyncQueryWorker.java create mode 100644 bulletin-board-client/src/test/java/meerkat/bulletinboard/CachedBulletinBoardClientTest.java create mode 100644 bulletin-board-client/src/test/java/meerkat/bulletinboard/SingleServerBulletinBoardClientIntegrationTest.java diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java index 6594b0c..cdb86cb 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java @@ -23,12 +23,16 @@ import java.util.List; public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClient { private final AsyncBulletinBoardClient localClient; - private AsyncBulletinBoardClient remoteClient; - private BulletinBoardSubscriber subscriber; - private BulletinBoardSynchronizer synchronizer; + private final AsyncBulletinBoardClient remoteClient; + private final AsyncBulletinBoardClient queueClient; + private final BulletinBoardSubscriber subscriber; + private final BulletinBoardSynchronizer synchronizer; private Thread syncThread; + private final static int DEFAULT_WAIT_CAP = 3000; + private final static int DEFAULT_SLEEP_INTERVAL = 3000; + private class SubscriptionStoreCallback implements FutureCallback> { private final FutureCallback> callback; @@ -81,21 +85,36 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien * @param localClient is a Client for the local instance * @param remoteClient is a Client for the remote instance(s); Should have endless retries for post operations * @param subscriber is a subscription service to the remote instance(s) - * @param queue is a client for a local deletable server to be used as a queue for not-yet-uploaded messages + * @param queueClient is a client for a local deletable server to be used as a queue for not-yet-uploaded messages */ + public CachedBulletinBoardClient(AsyncBulletinBoardClient localClient, + AsyncBulletinBoardClient remoteClient, + BulletinBoardSubscriber subscriber, + DeletableSubscriptionBulletinBoardClient queueClient, + int sleepInterval, + int waitCap) { + + this.localClient = localClient; + this.remoteClient = remoteClient; + this.subscriber = subscriber; + this.queueClient = queueClient; + + this.synchronizer = new SimpleBulletinBoardSynchronizer(sleepInterval,waitCap); + synchronizer.init(queueClient, remoteClient); + syncThread = new Thread(synchronizer); + syncThread.start(); + } + + /** + * Creates a Cached Client + * Used default values foe the time caps + * */ public CachedBulletinBoardClient(AsyncBulletinBoardClient localClient, AsyncBulletinBoardClient remoteClient, BulletinBoardSubscriber subscriber, DeletableSubscriptionBulletinBoardClient queue) { - this.localClient = localClient; - this.remoteClient = remoteClient; - this.subscriber = subscriber; - - this.synchronizer = new SimpleBulletinBoardSynchronizer(); - synchronizer.init(queue, remoteClient); - syncThread = new Thread(synchronizer); - syncThread.start(); + this(localClient, remoteClient, subscriber, queue, DEFAULT_SLEEP_INTERVAL, DEFAULT_WAIT_CAP); } @Override @@ -385,7 +404,7 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien } @Override - public float getRedundancy(MessageID id) { + public float getRedundancy(MessageID id) throws CommunicationException { return remoteClient.getRedundancy(id); } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java index 521815d..a274f53 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java @@ -68,16 +68,11 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{ // Post message to all databases try { for (String db : meerkatDBs) { - webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(POST_MESSAGE_PATH); - 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(BoolValue.class).getValue(); - } else { - throw new CommunicationException("Server returned error. Status was: " + response.getStatus()); - } + SingleServerPostMessageWorker worker = new SingleServerPostMessageWorker(db, msg, 0); + + worker.call(); + } } catch (Exception e) { // Occurs only when server replies with valid status but invalid data throw new CommunicationException("Error accessing database: " + e.getMessage()); diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java index 86ede70..be75dfe 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java @@ -8,15 +8,15 @@ import com.google.protobuf.Int64Value; import com.google.protobuf.Timestamp; import meerkat.bulletinboard.workers.singleserver.*; import meerkat.comm.CommunicationException; -import meerkat.protobuf.BulletinBoardAPI; +import meerkat.crypto.concrete.SHA256Digest; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.protobuf.Crypto; import meerkat.protobuf.Voting.BulletinBoardClientParams; import meerkat.util.BulletinBoardUtils; +import javax.ws.rs.client.Client; import java.lang.Iterable; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -31,11 +31,17 @@ import java.util.concurrent.atomic.AtomicInteger; * If the list of servers contains more than one server: the server actually used is the first one * The class further implements a delayed access to the server after a communication error occurs */ -public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient implements SubscriptionBulletinBoardClient { +public class SingleServerBulletinBoardClient implements SubscriptionBulletinBoardClient { + + protected Client client; + + protected BulletinBoardDigest digest; + + private String dbAddress; private final int MAX_RETRIES = 11; - private ListeningScheduledExecutorService executorService; + private final ListeningScheduledExecutorService executorService; private long lastServerErrorTime; @@ -54,6 +60,43 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i } + private class SynchronousRetry { + + private final SingleServerWorker worker; + + private String thrown; + + public SynchronousRetry(SingleServerWorker worker) { + this.worker = worker; + this.thrown = "Could not contact server. Errors follow:\n"; + } + + OUT run() throws CommunicationException { + + do { + + try { + return worker.call(); + } catch (Exception e) { + thrown += e.getCause() + " " + e.getMessage() + "\n"; + } + + try { + Thread.sleep(failDelayInMilliseconds); + } catch (InterruptedException e) { + //TODO: log + } + + worker.decMaxRetry(); + + } while (worker.isRetry()); + + throw new CommunicationException(thrown); + + } + + } + /** * This method adds a worker to the scheduled queue of the threadpool * If the server is in an accessible state: the job is submitted for immediate handling @@ -225,7 +268,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i .build()) .build(); - SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(meerkatDBs.get(0), batchQuery, MAX_RETRIES); + SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(dbAddress, batchQuery, MAX_RETRIES); scheduleWorker(batchWorker, new ReadBatchCallback(msg, callback)); @@ -266,20 +309,28 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i if (callback != null) callback.onSuccess(result); - // Remove last filter from list (MIN_ENTRY one) - filterBuilder.removeFilter(filterBuilder.getFilterCount() - 1); + // Update filter if needed - // Add updated MIN_ENTRY filter (entry number is successor of last received entry's number) - filterBuilder.addFilter(MessageFilter.newBuilder() - .setType(FilterType.MIN_ENTRY) - .setEntry(result.get(result.size() - 1).getEntryNum() + 1) - .build()); + if (result.size() > 0) { + + // Remove last filter from list (MIN_ENTRY one) + filterBuilder.removeFilter(filterBuilder.getFilterCount() - 1); + + // Add updated MIN_ENTRY filter (entry number is successor of last received entry's number) + filterBuilder.addFilter(MessageFilter.newBuilder() + .setType(FilterType.MIN_ENTRY) + .setEntry(result.get(result.size() - 1).getEntryNum() + 1) + .build()); + + } // Create new worker with updated task - worker = new SingleServerReadMessagesWorker(worker.serverAddress, filterBuilder.build(), 1); + worker = new SingleServerReadMessagesWorker(worker.serverAddress, filterBuilder.build(), MAX_RETRIES); - // Schedule the worker - scheduleWorker(worker, this); + RetryCallback> retryCallback = new RetryCallback<>(worker, this); + + // Schedule the worker to run after the given interval has elapsed + Futures.addCallback(executorService.schedule(worker, subscriptionIntervalInMilliseconds, TimeUnit.MILLISECONDS), retryCallback); } @@ -324,21 +375,192 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i @Override public void init(BulletinBoardClientParams clientParams) { - // Perform usual setup - super.init(clientParams); + this.digest = new GenericBulletinBoardDigest(new SHA256Digest()); // Remove all but first DB address - String dbAddress = meerkatDBs.get(0); - meerkatDBs = new LinkedList<>(); - meerkatDBs.add(dbAddress); + this.dbAddress = clientParams.getBulletinBoardAddress(0); } + // Synchronous methods + + @Override + public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { + + SingleServerPostMessageWorker worker = new SingleServerPostMessageWorker(dbAddress, msg, MAX_RETRIES); + + SynchronousRetry retry = new SynchronousRetry<>(worker); + + retry.run(); + + digest.reset(); + digest.update(msg); + + return digest.digestAsMessageID(); + + } + + @Override + public float getRedundancy(MessageID id) throws CommunicationException { + + SingleServerGetRedundancyWorker worker = new SingleServerGetRedundancyWorker(dbAddress, id, MAX_RETRIES); + + SynchronousRetry retry = new SynchronousRetry<>(worker); + + return retry.run(); + + } + + @Override + public List readMessages(MessageFilterList filterList) throws CommunicationException { + + SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(dbAddress, filterList, MAX_RETRIES); + + SynchronousRetry> retry = new SynchronousRetry<>(worker); + + return retry.run(); + + } + + @Override + public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException { + + // Begin the batch and obtain identifier + + BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder() + .addAllTag(msg.getMsg().getTagList()) + .build(); + + SingleServerBeginBatchWorker beginBatchWorker = new SingleServerBeginBatchWorker(dbAddress, beginBatchMessage, MAX_RETRIES); + + SynchronousRetry beginRetry = new SynchronousRetry<>(beginBatchWorker); + + Int64Value identifier = beginRetry.run(); + + // Post data chunks + + List batchChunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize); + + BatchMessage.Builder builder = BatchMessage.newBuilder().setBatchId(identifier.getValue()); + + int position = 0; + + for (BatchChunk data : batchChunkList) { + + builder.setSerialNum(position).setData(data); + + SingleServerPostBatchWorker dataWorker = new SingleServerPostBatchWorker(dbAddress, builder.build(), MAX_RETRIES); + + SynchronousRetry dataRetry = new SynchronousRetry<>(dataWorker); + + dataRetry.run(); + + // Increment position in batch + position++; + + } + + // Close batch + + CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder() + .setBatchId(identifier.getValue()) + .addAllSig(msg.getSigList()) + .setTimestamp(msg.getMsg().getTimestamp()) + .setBatchLength(position) + .build(); + + SingleServerCloseBatchWorker closeBatchWorker = new SingleServerCloseBatchWorker(dbAddress, closeBatchMessage, MAX_RETRIES); + + SynchronousRetry retry = new SynchronousRetry<>(closeBatchWorker); + + retry.run(); + + // Calculate ID and return + + digest.reset(); + digest.update(msg); + + return digest.digestAsMessageID(); + + } + + @Override + public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException { + + // Retrieve message (which may be a stub) + + MessageFilterList filterList = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.MSG_ID) + .setId(msgID.getID()) + .build()) + .build(); + + SingleServerReadMessagesWorker stubWorker = new SingleServerReadMessagesWorker(dbAddress, filterList, MAX_RETRIES); + + SynchronousRetry> retry = new SynchronousRetry<>(stubWorker); + + List messages = retry.run(); + + if (messages.size() <= 0) { + throw new CommunicationException("Could not find message in database."); + } + + BulletinBoardMessage msg = messages.get(0); + + if (msg.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { + + // We retrieved a complete message. Return it. + return msg; + + } else { + + // We retrieved a stub. Retrieve data. + return readBatchData(msg); + + } + + } + + @Override + public BulletinBoardMessage readBatchData(BulletinBoardMessage stub) throws CommunicationException, IllegalArgumentException { + + BatchQuery batchQuery = BatchQuery.newBuilder() + .setMsgID(MessageID.newBuilder() + .setID(stub.getMsg().getMsgId()) + .build()) + .setStartPosition(0) + .build(); + + SingleServerReadBatchWorker readBatchWorker = new SingleServerReadBatchWorker(dbAddress, batchQuery, MAX_RETRIES); + + SynchronousRetry> batchRetry = new SynchronousRetry<>(readBatchWorker); + + List batchChunkList = batchRetry.run(); + + return BulletinBoardUtils.gatherBatch(stub, batchChunkList); + + } + + @Override + public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { + + SingleServerGenerateSyncQueryWorker worker = + new SingleServerGenerateSyncQueryWorker(dbAddress, generateSyncQueryParams, MAX_RETRIES); + + SynchronousRetry retry = new SynchronousRetry<>(worker); + + return retry.run(); + + } + + // Asynchronous methods + @Override public MessageID postMessage(BulletinBoardMessage msg, FutureCallback callback) { // Create worker with redundancy 1 and MAX_RETRIES retries - SingleServerPostMessageWorker worker = new SingleServerPostMessageWorker(meerkatDBs.get(0), msg, MAX_RETRIES); + SingleServerPostMessageWorker worker = new SingleServerPostMessageWorker(dbAddress, msg, MAX_RETRIES); // Submit worker and create callback scheduleWorker(worker, new RetryCallback<>(worker, callback)); @@ -453,7 +675,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i // Create worker with redundancy 1 and MAX_RETRIES retries SingleServerBeginBatchWorker worker = - new SingleServerBeginBatchWorker(meerkatDBs.get(0), beginBatchMessage, MAX_RETRIES); + new SingleServerBeginBatchWorker(dbAddress, beginBatchMessage, MAX_RETRIES); // Submit worker and create callback scheduleWorker(worker, new RetryCallback<>(worker, new BeginBatchCallback(callback))); @@ -491,7 +713,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i // Create worker with redundancy 1 and MAX_RETRIES retries SingleServerPostBatchWorker worker = - new SingleServerPostBatchWorker(meerkatDBs.get(0), builder.build(), MAX_RETRIES); + new SingleServerPostBatchWorker(dbAddress, builder.build(), MAX_RETRIES); // Create worker with redundancy 1 and MAX_RETRIES retries scheduleWorker(worker, new RetryCallback<>(worker, listCallback)); @@ -529,7 +751,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i // Create worker with redundancy 1 and MAX_RETRIES retries SingleServerCloseBatchWorker worker = - new SingleServerCloseBatchWorker(meerkatDBs.get(0), closeBatchMessage, MAX_RETRIES); + new SingleServerCloseBatchWorker(dbAddress, closeBatchMessage, MAX_RETRIES); // Submit worker and create callback scheduleWorker(worker, new RetryCallback<>(worker, callback)); @@ -540,7 +762,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i public void getRedundancy(MessageID id, FutureCallback callback) { // Create worker with no retries - SingleServerGetRedundancyWorker worker = new SingleServerGetRedundancyWorker(meerkatDBs.get(0), id, 1); + SingleServerGetRedundancyWorker worker = new SingleServerGetRedundancyWorker(dbAddress, id, 1); // Submit job and create callback scheduleWorker(worker, new RetryCallback<>(worker, callback)); @@ -551,7 +773,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i public void readMessages(MessageFilterList filterList, FutureCallback> callback) { // Create job with no retries - SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(meerkatDBs.get(0), filterList, 1); + SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(dbAddress, filterList, 1); // Submit job and create callback scheduleWorker(worker, new RetryCallback<>(worker, callback)); @@ -575,7 +797,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i .setStartPosition(0) .build(); - SingleServerReadMessagesWorker messageWorker = new SingleServerReadMessagesWorker(meerkatDBs.get(0), filterList, MAX_RETRIES); + SingleServerReadMessagesWorker messageWorker = new SingleServerReadMessagesWorker(dbAddress, filterList, MAX_RETRIES); // Submit jobs with wrapped callbacks scheduleWorker(messageWorker, new RetryCallback<>(messageWorker, new CompleteMessageReadCallback(callback))); @@ -598,7 +820,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i .setStartPosition(0) .build(); - SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(meerkatDBs.get(0), batchQuery, MAX_RETRIES); + SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(dbAddress, batchQuery, MAX_RETRIES); scheduleWorker(batchWorker, new RetryCallback<>(batchWorker, new ReadBatchCallback(stub, callback))); @@ -607,7 +829,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i @Override public void querySync(SyncQuery syncQuery, FutureCallback callback) { - SingleServerQuerySyncWorker worker = new SingleServerQuerySyncWorker(meerkatDBs.get(0), syncQuery, MAX_RETRIES); + SingleServerQuerySyncWorker worker = new SingleServerQuerySyncWorker(dbAddress, syncQuery, MAX_RETRIES); scheduleWorker(worker, new RetryCallback<>(worker, callback)); @@ -633,7 +855,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i .build()); // Create job with no retries - SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(meerkatDBs.get(0), filterListBuilder.build(), MAX_RETRIES); + SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(dbAddress, filterListBuilder.build(), MAX_RETRIES); // Submit job and create callback that retries on failure and handles repeated subscription scheduleWorker(worker, new RetryCallback<>(worker, new SubscriptionCallback(worker, callback))); @@ -647,8 +869,6 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i @Override public void close() { - super.close(); - executorService.shutdown(); } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java index 9568255..252d6e3 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java @@ -250,7 +250,6 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber super(filterList, callback); } - @Override public void onSuccess(List result) { @@ -274,5 +273,4 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber subscribe(filterList, 0, callback); } - } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGenerateSyncQueryWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGenerateSyncQueryWorker.java new file mode 100644 index 0000000..71d90e0 --- /dev/null +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGenerateSyncQueryWorker.java @@ -0,0 +1,55 @@ +package meerkat.bulletinboard.workers.singleserver; + +import com.google.protobuf.Int64Value; +import meerkat.bulletinboard.SingleServerWorker; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.BulletinBoardAPI.SyncQuery; +import meerkat.protobuf.BulletinBoardAPI.GenerateSyncQueryParams; +import meerkat.rest.Constants; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; + +import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH; +import static meerkat.bulletinboard.BulletinBoardConstants.GENERATE_SYNC_QUERY_PATH; + +/** + * Created by Arbel Deutsch Peled on 27-Dec-15. + * Tries to contact server once and perform a Sync Query Generation operation + */ +public class SingleServerGenerateSyncQueryWorker extends SingleServerWorker { + + public SingleServerGenerateSyncQueryWorker(String serverAddress, GenerateSyncQueryParams payload, int maxRetry) { + super(serverAddress, payload, maxRetry); + } + + @Override + public SyncQuery call() throws Exception { + + Client client = clientLocal.get(); + + WebTarget webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(GENERATE_SYNC_QUERY_PATH); + + Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF)); + + try { + + SyncQuery result = response.readEntity(SyncQuery.class); + return result; + + } catch (ProcessingException | IllegalStateException e) { + + // Post to this server failed + throw new CommunicationException("Could not contact the server. Original error: " + e.getMessage()); + + } catch (Exception e) { + throw new CommunicationException("Could not contact the server. Original error: " + e.getMessage()); + } + finally { + response.close(); + } + } +} diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java index 588f529..19e7ae5 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java @@ -114,7 +114,7 @@ public class BulletinBoardSynchronizerTest { public void init() throws CommunicationException { DeletableBulletinBoardServer remoteServer = new BulletinBoardSQLServer(new H2QueryProvider(REMOTE_SERVER_ADDRESS + testCount)); - remoteServer.init(REMOTE_SERVER_ADDRESS); + remoteServer.init(); remoteClient = new LocalBulletinBoardClient( remoteServer, @@ -122,7 +122,7 @@ public class BulletinBoardSynchronizerTest { SUBSCRIPTION_INTERVAL); DeletableBulletinBoardServer localServer = new BulletinBoardSQLServer(new H2QueryProvider(LOCAL_SERVER_ADDRESS + testCount)); - localServer.init(LOCAL_SERVER_ADDRESS); + localServer.init(); localClient = new LocalBulletinBoardClient( localServer, diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/CachedBulletinBoardClientTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/CachedBulletinBoardClientTest.java new file mode 100644 index 0000000..83e62ff --- /dev/null +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/CachedBulletinBoardClientTest.java @@ -0,0 +1,111 @@ +package meerkat.bulletinboard; + +import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; +import meerkat.bulletinboard.sqlserver.H2QueryProvider; +import meerkat.comm.CommunicationException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.security.SignatureException; +import java.util.LinkedList; +import java.util.List; + +/** + * Created by Arbel on 6/27/2016. + */ +public class CachedBulletinBoardClientTest { + + private static final int THREAD_NUM = 3; + + private static final String LOCAL_DB_NAME = "localDB"; + private static final String REMOTE_DB_NAME = "remoteDB"; + private static final String QUEUE_DB_NAME = "queueDB"; + + private static final int SUBSRCIPTION_DELAY = 500; + private static final int SYNC_DELAY = 500; + + // Testers + private CachedBulletinBoardClient cachedClient; + private GenericBulletinBoardClientTester clientTest; + private GenericSubscriptionClientTester subscriptionTester; + + public CachedBulletinBoardClientTest() throws CommunicationException { + + DeletableBulletinBoardServer localServer = new BulletinBoardSQLServer(new H2QueryProvider(LOCAL_DB_NAME)); + localServer.init(); + LocalBulletinBoardClient localClient = new LocalBulletinBoardClient(localServer, THREAD_NUM, SUBSRCIPTION_DELAY); + + DeletableBulletinBoardServer remoteServer = new BulletinBoardSQLServer(new H2QueryProvider(REMOTE_DB_NAME)); + remoteServer.init(); + LocalBulletinBoardClient remoteClient = new LocalBulletinBoardClient(remoteServer, THREAD_NUM, SUBSRCIPTION_DELAY); + + DeletableBulletinBoardServer queueServer = new BulletinBoardSQLServer(new H2QueryProvider(QUEUE_DB_NAME)); + queueServer.init(); + LocalBulletinBoardClient queueClient = new LocalBulletinBoardClient(queueServer, THREAD_NUM, SUBSRCIPTION_DELAY); + + List clientList = new LinkedList<>(); + clientList.add(remoteClient); + + BulletinBoardSubscriber subscriber = new ThreadedBulletinBoardSubscriber(clientList, localClient); + + cachedClient = new CachedBulletinBoardClient(localClient, remoteClient, subscriber, queueClient, SYNC_DELAY, SYNC_DELAY); + subscriptionTester = new GenericSubscriptionClientTester(cachedClient); + clientTest = new GenericBulletinBoardClientTester(cachedClient); + + } + + // Test methods + + /** + * Takes care of initializing the client and the test resources + */ + @Before + public void init(){ + + clientTest.init(); + + } + + /** + * Closes the client and makes sure the test fails when an exception occurred in a separate thread + */ + + @After + public void close() { + + cachedClient.close(); + clientTest.close(); + + } + + @Test + public void testPost() { + + clientTest.testPost(); + + } + + @Test + public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException { + + clientTest.testBatchPost(); + } + + @Test + public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException { + + clientTest.testCompleteBatchPost(); + + } + + @Test + public void testSubscription() throws SignatureException, CommunicationException { + +// subscriptionTester.init(); +// subscriptionTester.subscriptionTest(); +// subscriptionTester.close(); + + } + +} diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java index ea74885..6d02914 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java @@ -178,7 +178,7 @@ public class GenericSubscriptionClientTester { public void onFailure(Throwable t) { System.err.println(t.getCause() + " " + t.getMessage()); thrown.add(t); - jobSemaphore.release(expectedMessages.size()); + jobSemaphore.release(); stage = expectedMessages.size(); } } @@ -202,9 +202,9 @@ public class GenericSubscriptionClientTester { .build(); List> expectedMessages = new ArrayList<>(3); - expectedMessages.add(new LinkedList()); - expectedMessages.add(new LinkedList()); - expectedMessages.add(new LinkedList()); + expectedMessages.add(new LinkedList<>()); + expectedMessages.add(new LinkedList<>()); + expectedMessages.add(new LinkedList<>()); expectedMessages.get(0).add(msg1); expectedMessages.get(2).add(msg3); diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java index 56392d7..9ee1d60 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java @@ -7,12 +7,6 @@ import org.junit.Before; import org.junit.Test; import java.security.SignatureException; -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.List; - -import static org.junit.Assert.fail; /** * Created by Arbel Deutsch Peled on 05-Dec-15. @@ -30,26 +24,10 @@ public class LocalBulletinBoardClientTest { public LocalBulletinBoardClientTest() throws CommunicationException { - H2QueryProvider queryProvider = new H2QueryProvider(DB_NAME) ; - - try { - - Connection conn = queryProvider.getDataSource().getConnection(); - Statement stmt = conn.createStatement(); - - List deletionQueries = queryProvider.getSchemaDeletionCommands(); - - for (String deletionQuery : deletionQueries) { - stmt.execute(deletionQuery); - } - - } catch (SQLException e) { - System.err.println(e.getMessage()); - throw new CommunicationException(e.getCause() + " " + e.getMessage()); - } + H2QueryProvider queryProvider = new H2QueryProvider(DB_NAME); DeletableBulletinBoardServer server = new BulletinBoardSQLServer(queryProvider); - server.init(DB_NAME); + server.init(); LocalBulletinBoardClient client = new LocalBulletinBoardClient(server, THREAD_NUM, SUBSRCIPTION_DELAY); subscriptionTester = new GenericSubscriptionClientTester(client); @@ -102,6 +80,7 @@ public class LocalBulletinBoardClientTest { @Test public void testSubscription() throws SignatureException, CommunicationException { + subscriptionTester.init(); subscriptionTester.subscriptionTest(); subscriptionTester.close(); diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/SingleServerBulletinBoardClientIntegrationTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/SingleServerBulletinBoardClientIntegrationTest.java new file mode 100644 index 0000000..424c4c5 --- /dev/null +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/SingleServerBulletinBoardClientIntegrationTest.java @@ -0,0 +1,104 @@ +package meerkat.bulletinboard; + +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.Voting.BulletinBoardClientParams; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.security.SignatureException; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Executors; + +/** + * Created by Arbel Deutsch Peled on 05-Dec-15. + */ +public class SingleServerBulletinBoardClientIntegrationTest { + + // Server data + + private static final String PROP_GETTY_URL = "gretty.httpBaseURI"; + private static final String DEFAULT_BASE_URL = "http://localhost:8081"; + private static final String BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL); + + private static final int THREAD_NUM = 3; + private static final long FAIL_DELAY = 3000; + private static final long SUBSCRIPTION_INTERVAL = 500; + + // Testers + private GenericBulletinBoardClientTester clientTest; + private GenericSubscriptionClientTester subscriptionTester; + + public SingleServerBulletinBoardClientIntegrationTest(){ + + SingleServerBulletinBoardClient client = new SingleServerBulletinBoardClient(THREAD_NUM, FAIL_DELAY, SUBSCRIPTION_INTERVAL); + + List testDB = new LinkedList<>(); + testDB.add(BASE_URL); + + client.init(BulletinBoardClientParams.newBuilder() + .addAllBulletinBoardAddress(testDB) + .setMinRedundancy((float) 1.0) + .build()); + + clientTest = new GenericBulletinBoardClientTester(client); + subscriptionTester = new GenericSubscriptionClientTester(client); + + } + + // Test methods + + /** + * Takes care of initializing the client and the test resources + */ + @Before + public void init(){ + + clientTest.init(); + + } + + /** + * Closes the client and makes sure the test fails when an exception occurred in a separate thread + */ + + @After + public void close() { + + clientTest.close(); + + } + + @Test + public void testPost() { + + clientTest.testPost(); + + } + + @Test + public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException { + + clientTest.testBatchPost(); + } + + @Test + public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException { + + clientTest.testCompleteBatchPost(); + + } + + @Test + public void testSubscription() throws SignatureException, CommunicationException { + + subscriptionTester.init(); + subscriptionTester.subscriptionTest(); + subscriptionTester.close(); + + } + +} diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java index e9c910a..bf99259 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java @@ -368,7 +368,7 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ * This method initializes the signatures, connects to the DB and creates the schema (if required). */ @Override - public void init(String meerkatDB) throws CommunicationException { + public void init() throws CommunicationException { // TODO write signature reading part. digest = new GenericBulletinBoardDigest(new SHA256Digest()); diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java index f9d3a94..8e9d161 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java @@ -44,14 +44,6 @@ public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextL bulletinBoard = (BulletinBoardServer) servletContext.getAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME); } - /** - * This is the BulletinBoard init method. - */ - @Override - public void init(String meerkatDB) throws CommunicationException { - bulletinBoard.init(meerkatDB); - } - @Override public void contextInitialized(ServletContextEvent servletContextEvent) { ServletContext servletContext = servletContextEvent.getServletContext(); @@ -77,7 +69,7 @@ public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextL } try { - init(dbName); + bulletinBoard.init(); servletContext.setAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME, bulletinBoard); } catch (CommunicationException e) { System.err.println(e.getMessage()); diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/H2BulletinBoardServerTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/H2BulletinBoardServerTest.java index fa96635..def0b41 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/H2BulletinBoardServerTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/H2BulletinBoardServerTest.java @@ -54,7 +54,7 @@ public class H2BulletinBoardServerTest { BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(queryProvider); try { - bulletinBoardServer.init(""); + bulletinBoardServer.init(); } catch (CommunicationException e) { System.err.println(e.getMessage()); diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java index 334620c..abc0fc6 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java @@ -58,7 +58,7 @@ public class MySQLBulletinBoardServerTest { BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(queryProvider); try { - bulletinBoardServer.init(""); + bulletinBoardServer.init(); } catch (CommunicationException e) { System.err.println(e.getMessage()); diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteBulletinBoardServerTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteBulletinBoardServerTest.java index 4dcd97b..18278b3 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteBulletinBoardServerTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteBulletinBoardServerTest.java @@ -39,7 +39,7 @@ public class SQLiteBulletinBoardServerTest{ BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(new SQLiteQueryProvider(testFilename)); try { - bulletinBoardServer.init(""); + bulletinBoardServer.init(); } catch (CommunicationException e) { System.err.println(e.getMessage()); diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java index 7f29608..c03051c 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java @@ -31,8 +31,9 @@ public interface BulletinBoardClient { * Check how "safe" a given message is in a synchronous manner * @param id is the unique message identifier for retrieval * @return a normalized "redundancy score" from 0 (local only) to 1 (fully published) + * @throws CommunicationException */ - float getRedundancy(MessageID id); + float getRedundancy(MessageID id) throws CommunicationException; /** * Read all messages posted matching the given filter in a synchronous manner diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java index 8121b85..40f8ab3 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java @@ -22,7 +22,7 @@ public interface BulletinBoardServer{ * It also establishes the connection to the DB * @throws CommunicationException on DB connection error */ - public void init(String meerkatDB) throws CommunicationException; + public void init() throws CommunicationException; /** * Post a message to bulletin board. From 8aada211191be8a650275c602431df10c5261f70 Mon Sep 17 00:00:00 2001 From: "arbel.peled" Date: Tue, 28 Jun 2016 15:08:36 +0300 Subject: [PATCH 14/15] Fixed some errors in the tests. Made Threaded Client parameterized (with respect to waiting times and thread count). --- .../SingleServerBulletinBoardClient.java | 16 ++++++------- .../ThreadedBulletinBoardClient.java | 24 ++++++++++++++----- .../MultiServerReadBatchDataWorker.java | 8 +++---- .../CachedBulletinBoardClientTest.java | 2 +- .../GenericBulletinBoardClientTester.java | 5 ++-- .../LocalBulletinBoardClientTest.java | 2 +- ...verBulletinBoardClientIntegrationTest.java | 2 +- ...dedBulletinBoardClientIntegrationTest.java | 5 ++-- 8 files changed, 38 insertions(+), 26 deletions(-) diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java index be75dfe..e732059 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java @@ -45,9 +45,9 @@ public class SingleServerBulletinBoardClient implements SubscriptionBulletinBoar private long lastServerErrorTime; - private final long failDelayInMilliseconds; + private final long FAIL_DELAY_IN_MILLISECONDS; - private final long subscriptionIntervalInMilliseconds; + private final long SUBSCRIPTION_INTERVAL_IN_MILLISECONDS; /** * Notify the client that a job has failed @@ -82,7 +82,7 @@ public class SingleServerBulletinBoardClient implements SubscriptionBulletinBoar } try { - Thread.sleep(failDelayInMilliseconds); + Thread.sleep(FAIL_DELAY_IN_MILLISECONDS); } catch (InterruptedException e) { //TODO: log } @@ -108,7 +108,7 @@ public class SingleServerBulletinBoardClient implements SubscriptionBulletinBoar long timeSinceLastServerError = System.currentTimeMillis() - lastServerErrorTime; - if (timeSinceLastServerError >= failDelayInMilliseconds) { + if (timeSinceLastServerError >= FAIL_DELAY_IN_MILLISECONDS) { // Schedule for immediate processing Futures.addCallback(executorService.submit(worker), callback); @@ -118,7 +118,7 @@ public class SingleServerBulletinBoardClient implements SubscriptionBulletinBoar // Schedule for processing immediately following delay expiry Futures.addCallback(executorService.schedule( worker, - failDelayInMilliseconds - timeSinceLastServerError, + FAIL_DELAY_IN_MILLISECONDS - timeSinceLastServerError, TimeUnit.MILLISECONDS), callback); @@ -330,7 +330,7 @@ public class SingleServerBulletinBoardClient implements SubscriptionBulletinBoar RetryCallback> retryCallback = new RetryCallback<>(worker, this); // Schedule the worker to run after the given interval has elapsed - Futures.addCallback(executorService.schedule(worker, subscriptionIntervalInMilliseconds, TimeUnit.MILLISECONDS), retryCallback); + Futures.addCallback(executorService.schedule(worker, SUBSCRIPTION_INTERVAL_IN_MILLISECONDS, TimeUnit.MILLISECONDS), retryCallback); } @@ -352,8 +352,8 @@ public class SingleServerBulletinBoardClient implements SubscriptionBulletinBoar this.executorService = executorService; - this.failDelayInMilliseconds = failDelayInMilliseconds; - this.subscriptionIntervalInMilliseconds = subscriptionIntervalInMilliseconds; + this.FAIL_DELAY_IN_MILLISECONDS = failDelayInMilliseconds; + this.SUBSCRIPTION_INTERVAL_IN_MILLISECONDS = subscriptionIntervalInMilliseconds; // Set server error time to a time sufficiently in the past to make new jobs go through lastServerErrorTime = System.currentTimeMillis() - failDelayInMilliseconds; diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java index b549a2e..9dd85b9 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java @@ -36,13 +36,27 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple private final static int READ_MESSAGES_RETRY_NUM = 1; private final static int GET_REDUNDANCY_RETRY_NUM = 1; - private static final int SERVER_THREADPOOL_SIZE = 5; - private static final long FAIL_DELAY = 5000; - private static final long SUBSCRIPTION_INTERVAL = 10000; + private final int SERVER_THREADPOOL_SIZE; + private final long FAIL_DELAY; + private final long SUBSCRIPTION_INTERVAL; + + private static final int DEFAULT_SERVER_THREADPOOL_SIZE = 5; + private static final long DEFAULT_FAIL_DELAY = 5000; + private static final long DEFAULT_SUBSCRIPTION_INTERVAL = 10000; private int minAbsoluteRedundancy; + public ThreadedBulletinBoardClient(int serverThreadpoolSize, long failDelay, long subscriptionInterval) { + SERVER_THREADPOOL_SIZE = serverThreadpoolSize; + FAIL_DELAY = failDelay; + SUBSCRIPTION_INTERVAL = subscriptionInterval; + } + + public ThreadedBulletinBoardClient() { + this(DEFAULT_SERVER_THREADPOOL_SIZE, DEFAULT_FAIL_DELAY, DEFAULT_SUBSCRIPTION_INTERVAL); + } + /** * Stores database locations and initializes the web Client * Stores the required minimum redundancy. @@ -232,11 +246,9 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); } - MessageID msgID = MessageID.newBuilder().setID(stub.getMsg().getMsgId()).build(); - // Create job MultiServerReadBatchDataWorker worker = - new MultiServerReadBatchDataWorker(clients, minAbsoluteRedundancy, msgID, READ_MESSAGES_RETRY_NUM, callback); + new MultiServerReadBatchDataWorker(clients, minAbsoluteRedundancy, stub, READ_MESSAGES_RETRY_NUM, callback); // Submit job executorService.submit(worker); diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java index ab5fd40..769702a 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java @@ -10,10 +10,10 @@ import java.util.List; /** * Created by Arbel Deutsch Peled on 27-Dec-15. */ -public class MultiServerReadBatchDataWorker extends MultiServerGenericReadWorker { +public class MultiServerReadBatchDataWorker extends MultiServerGenericReadWorker { public MultiServerReadBatchDataWorker(List clients, - int minServers, MessageID payload, int maxRetry, + int minServers, BulletinBoardMessage payload, int maxRetry, FutureCallback futureCallback) { super(clients, minServers, payload, maxRetry, futureCallback); @@ -21,8 +21,8 @@ public class MultiServerReadBatchDataWorker extends MultiServerGenericReadWorker } @Override - protected void doRead(MessageID payload, SingleServerBulletinBoardClient client) { - client.readMessage(payload, this); + protected void doRead(BulletinBoardMessage payload, SingleServerBulletinBoardClient client) { + client.readBatchData(payload, this); } diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/CachedBulletinBoardClientTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/CachedBulletinBoardClientTest.java index 83e62ff..8bae8c2 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/CachedBulletinBoardClientTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/CachedBulletinBoardClientTest.java @@ -51,7 +51,7 @@ public class CachedBulletinBoardClientTest { cachedClient = new CachedBulletinBoardClient(localClient, remoteClient, subscriber, queueClient, SYNC_DELAY, SYNC_DELAY); subscriptionTester = new GenericSubscriptionClientTester(cachedClient); - clientTest = new GenericBulletinBoardClientTester(cachedClient); + clientTest = new GenericBulletinBoardClientTester(cachedClient, 87351); } diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericBulletinBoardClientTester.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericBulletinBoardClientTester.java index f7a4653..7ded2e4 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericBulletinBoardClientTester.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericBulletinBoardClientTester.java @@ -48,7 +48,6 @@ public class GenericBulletinBoardClientTester { private AsyncBulletinBoardClient bulletinBoardClient; private PostCallback postCallback; - private PostCallback failPostCallback = new PostCallback(true,false); private RedundancyCallback redundancyCallback; private ReadCallback readCallback; @@ -64,7 +63,7 @@ public class GenericBulletinBoardClientTester { // Constructor - public GenericBulletinBoardClientTester(AsyncBulletinBoardClient bulletinBoardClient){ + public GenericBulletinBoardClientTester(AsyncBulletinBoardClient bulletinBoardClient, int seed){ this.bulletinBoardClient = bulletinBoardClient; @@ -113,7 +112,7 @@ public class GenericBulletinBoardClientTester { fail("Couldn't find signing key " + e.getMessage()); } - this.random = new Random(0); + this.random = new Random(seed); this.generator = new BulletinBoardMessageGenerator(random); this.digest = new GenericBulletinBoardDigest(new SHA256Digest()); diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java index 9ee1d60..d33d5ba 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java @@ -31,7 +31,7 @@ public class LocalBulletinBoardClientTest { LocalBulletinBoardClient client = new LocalBulletinBoardClient(server, THREAD_NUM, SUBSRCIPTION_DELAY); subscriptionTester = new GenericSubscriptionClientTester(client); - clientTest = new GenericBulletinBoardClientTester(client); + clientTest = new GenericBulletinBoardClientTester(client, 98354); } diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/SingleServerBulletinBoardClientIntegrationTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/SingleServerBulletinBoardClientIntegrationTest.java index 424c4c5..7c8d58b 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/SingleServerBulletinBoardClientIntegrationTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/SingleServerBulletinBoardClientIntegrationTest.java @@ -44,7 +44,7 @@ public class SingleServerBulletinBoardClientIntegrationTest { .setMinRedundancy((float) 1.0) .build()); - clientTest = new GenericBulletinBoardClientTester(client); + clientTest = new GenericBulletinBoardClientTester(client, 981541); subscriptionTester = new GenericSubscriptionClientTester(client); } diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java index dd47354..1187245 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java @@ -28,7 +28,7 @@ public class ThreadedBulletinBoardClientIntegrationTest { public ThreadedBulletinBoardClientIntegrationTest(){ - ThreadedBulletinBoardClient client = new ThreadedBulletinBoardClient(); + ThreadedBulletinBoardClient client = new ThreadedBulletinBoardClient(3,0,500); List testDB = new LinkedList<>(); testDB.add(BASE_URL); @@ -38,7 +38,7 @@ public class ThreadedBulletinBoardClientIntegrationTest { .setMinRedundancy((float) 1.0) .build()); - clientTest = new GenericBulletinBoardClientTester(client); + clientTest = new GenericBulletinBoardClientTester(client, 52351); } @@ -76,6 +76,7 @@ public class ThreadedBulletinBoardClientIntegrationTest { public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException { clientTest.testBatchPost(); + } @Test From 48bf8dbe6b25c86bc31cb04e5267b9db9524f6a3 Mon Sep 17 00:00:00 2001 From: "arbel.peled" Date: Tue, 28 Jun 2016 15:10:38 +0300 Subject: [PATCH 15/15] Ignore .arcconfig --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a14486c..3a97743 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ out *.classpath *.db *.sql +.arcconfig