Working client-side Batch changes
parent
1951db546d
commit
d1f7413cde
|
@ -3,9 +3,7 @@ package meerkat.bulletinboard;
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.protobuf.Timestamp;
|
import com.google.protobuf.Timestamp;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.protobuf.BulletinBoardAPI;
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.protobuf.Comm;
|
|
||||||
import meerkat.protobuf.Crypto.Signature;
|
import meerkat.protobuf.Crypto.Signature;
|
||||||
import meerkat.protobuf.Voting.*;
|
import meerkat.protobuf.Voting.*;
|
||||||
|
|
||||||
|
@ -267,9 +265,9 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readBatch(final MessageID msgID, final FutureCallback<BulletinBoardMessage> callback) {
|
public void readMessage(final MessageID msgID, final FutureCallback<BulletinBoardMessage> callback) {
|
||||||
|
|
||||||
localClient.readBatch(msgID, new FutureCallback<BulletinBoardMessage>() {
|
localClient.readMessage(msgID, new FutureCallback<BulletinBoardMessage>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(BulletinBoardMessage result) {
|
public void onSuccess(BulletinBoardMessage result) {
|
||||||
|
@ -282,7 +280,7 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien
|
||||||
|
|
||||||
// Read from local unsuccessful: try to read from remote
|
// Read from local unsuccessful: try to read from remote
|
||||||
|
|
||||||
remoteClient.readBatch(msgID, new FutureCallback<BulletinBoardMessage>() {
|
remoteClient.readMessage(msgID, new FutureCallback<BulletinBoardMessage>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(BulletinBoardMessage result) {
|
public void onSuccess(BulletinBoardMessage result) {
|
||||||
|
@ -398,17 +396,17 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BulletinBoardMessage readBatch(MessageID msgID) throws CommunicationException {
|
public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException {
|
||||||
|
|
||||||
BulletinBoardMessage result = null;
|
BulletinBoardMessage result = null;
|
||||||
try {
|
try {
|
||||||
result = localClient.readBatch(msgID);
|
result = localClient.readMessage(msgID);
|
||||||
} catch (CommunicationException e) {
|
} catch (CommunicationException e) {
|
||||||
//TODO: log
|
//TODO: log
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == null){
|
if (result == null){
|
||||||
result = remoteClient.readBatch(msgID);
|
result = remoteClient.readMessage(msgID);
|
||||||
|
|
||||||
if (result != null){
|
if (result != null){
|
||||||
localClient.postMessage(result);
|
localClient.postMessage(result);
|
||||||
|
|
|
@ -183,6 +183,8 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
batchId.setLength(i);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -244,6 +246,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
|
|
||||||
CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder()
|
CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder()
|
||||||
.setBatchId(identifier.getBatchId().getValue())
|
.setBatchId(identifier.getBatchId().getValue())
|
||||||
|
.setBatchLength(identifier.getLength())
|
||||||
.setTimestamp(timestamp)
|
.setTimestamp(timestamp)
|
||||||
.addAllSig(signatures)
|
.addAllSig(signatures)
|
||||||
.build();
|
.build();
|
||||||
|
@ -438,7 +441,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
@Override
|
@Override
|
||||||
public BulletinBoardMessage call() throws Exception {
|
public BulletinBoardMessage call() throws Exception {
|
||||||
|
|
||||||
// Read message stub
|
// Read message (mat be a stub)
|
||||||
|
|
||||||
MessageFilterList filterList = MessageFilterList.newBuilder()
|
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||||
.addFilter(MessageFilter.newBuilder()
|
.addFilter(MessageFilter.newBuilder()
|
||||||
|
@ -451,10 +454,12 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
List<BulletinBoardMessage> bulletinBoardMessages = messageReader.call();
|
List<BulletinBoardMessage> bulletinBoardMessages = messageReader.call();
|
||||||
|
|
||||||
if (bulletinBoardMessages.size() <= 0) {
|
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);
|
||||||
|
|
||||||
|
if (msg.getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.MSGID) {
|
||||||
|
|
||||||
// Read data
|
// Read data
|
||||||
|
|
||||||
|
@ -463,7 +468,11 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
|
|
||||||
// Combine and return
|
// Combine and return
|
||||||
|
|
||||||
return BulletinBoardUtils.gatherBatch(stub, batchChunkList);
|
return BulletinBoardUtils.gatherBatch(msg, batchChunkList);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -493,7 +502,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readBatch(MessageID msgID, FutureCallback<BulletinBoardMessage> callback) {
|
public void readMessage(MessageID msgID, FutureCallback<BulletinBoardMessage> callback) {
|
||||||
Futures.addCallback(executorService.submit(new CompleteBatchReader(msgID)), callback);
|
Futures.addCallback(executorService.submit(new CompleteBatchReader(msgID)), callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,7 +600,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BulletinBoardMessage readBatch(MessageID msgID) throws CommunicationException {
|
public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException {
|
||||||
|
|
||||||
MessageFilterList filterList = MessageFilterList.newBuilder()
|
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||||
.addFilter(MessageFilter.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");
|
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();
|
BatchDataCombiner combiner = new BatchDataCombiner(stub);
|
||||||
|
|
||||||
BatchDataReader batchDataReader = new BatchDataReader(msgID);
|
|
||||||
|
|
||||||
List<BatchChunk> batchChunkList = null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
batchChunkList = batchDataReader.call();
|
return combiner.call();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new CommunicationException(e.getCause() + " " + e.getMessage());
|
throw new CommunicationException(e.getCause() + " " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return BulletinBoardUtils.gatherBatch(stub, batchChunkList);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -224,7 +224,7 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BulletinBoardMessage readBatch(MessageID msgID) throws CommunicationException {
|
public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException {
|
||||||
|
|
||||||
MessageFilterList filterList = MessageFilterList.newBuilder()
|
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||||
.addFilter(MessageFilter.newBuilder()
|
.addFilter(MessageFilter.newBuilder()
|
||||||
|
|
|
@ -36,75 +36,6 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize
|
||||||
|
|
||||||
private Semaphore semaphore;
|
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<Boolean> {
|
|
||||||
|
|
||||||
private final long entryNum;
|
|
||||||
private final FutureCallback<Void> callback;
|
|
||||||
|
|
||||||
public MessageDeleteCallback(long entryNum, FutureCallback<Void> 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<Void> {
|
|
||||||
|
|
||||||
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<List<BulletinBoardMessage>> {
|
private class SyncCallback implements FutureCallback<List<BulletinBoardMessage>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -122,8 +53,6 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize
|
||||||
|
|
||||||
// Handle upload and status change
|
// Handle upload and status change
|
||||||
|
|
||||||
SyncStatusUpdateCallback syncStatusUpdateCallback = new SyncStatusUpdateCallback(result.size());
|
|
||||||
|
|
||||||
SyncStatus newStatus = SyncStatus.PENDING;
|
SyncStatus newStatus = SyncStatus.PENDING;
|
||||||
|
|
||||||
if (result.size() == 0) {
|
if (result.size() == 0) {
|
||||||
|
@ -143,13 +72,17 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize
|
||||||
|
|
||||||
BulletinBoardMessage completeMsg = localClient.readBatchData(message);
|
BulletinBoardMessage completeMsg = localClient.readBatchData(message);
|
||||||
|
|
||||||
remoteClient.postMessage(completeMsg, new MessageDeleteCallback(message.getEntryNum(), syncStatusUpdateCallback));
|
remoteClient.postMessage(completeMsg);
|
||||||
|
|
||||||
|
localClient.deleteMessage(completeMsg.getEntryNum());
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// This is a regular message: post it
|
// This is a regular message: post it
|
||||||
remoteClient.postMessage(message, new MessageDeleteCallback(message.getEntryNum(), syncStatusUpdateCallback));
|
remoteClient.postMessage(message);
|
||||||
|
|
||||||
|
localClient.deleteMessage(message.getEntryNum());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ import com.google.common.util.concurrent.MoreExecutors;
|
||||||
import com.google.protobuf.Int64Value;
|
import com.google.protobuf.Int64Value;
|
||||||
import com.google.protobuf.Timestamp;
|
import com.google.protobuf.Timestamp;
|
||||||
import meerkat.bulletinboard.workers.singleserver.*;
|
import meerkat.bulletinboard.workers.singleserver.*;
|
||||||
|
import meerkat.comm.CommunicationException;
|
||||||
|
import meerkat.protobuf.BulletinBoardAPI;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.protobuf.Crypto;
|
import meerkat.protobuf.Crypto;
|
||||||
import meerkat.protobuf.Voting.BulletinBoardClientParams;
|
import meerkat.protobuf.Voting.BulletinBoardClientParams;
|
||||||
|
@ -166,91 +168,74 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private class ReadBatchCallback implements FutureCallback<List<BatchChunk>> {
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
class CompleteBatchReadCallback {
|
|
||||||
|
|
||||||
|
private final BulletinBoardMessage stub;
|
||||||
private final FutureCallback<BulletinBoardMessage> callback;
|
private final FutureCallback<BulletinBoardMessage> callback;
|
||||||
|
|
||||||
private List<BatchChunk> batchChunkList;
|
public ReadBatchCallback(BulletinBoardMessage stub, FutureCallback<BulletinBoardMessage> callback) {
|
||||||
private BulletinBoardMessage stub;
|
this.stub = stub;
|
||||||
|
|
||||||
private AtomicInteger remainingQueries;
|
|
||||||
private AtomicBoolean failed;
|
|
||||||
|
|
||||||
public CompleteBatchReadCallback(FutureCallback<BulletinBoardMessage> callback) {
|
|
||||||
|
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
|
||||||
remainingQueries = new AtomicInteger(2);
|
|
||||||
failed = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void combineAndReturn() {
|
|
||||||
|
|
||||||
if (remainingQueries.decrementAndGet() == 0){
|
|
||||||
|
|
||||||
if (callback != null)
|
|
||||||
callback.onSuccess(BulletinBoardUtils.gatherBatch(stub, batchChunkList));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void fail(Throwable t) {
|
|
||||||
if (failed.compareAndSet(false, true)) {
|
|
||||||
if (callback != null)
|
|
||||||
callback.onFailure(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a FutureCallback for the Batch Data List that ties to this object
|
|
||||||
*/
|
|
||||||
public FutureCallback<List<BatchChunk>> asBatchDataListFutureCallback() {
|
|
||||||
return new FutureCallback<List<BatchChunk>>() {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<BatchChunk> result) {
|
public void onSuccess(List<BatchChunk> result) {
|
||||||
batchChunkList = result;
|
callback.onSuccess(BulletinBoardUtils.gatherBatch(stub, result));
|
||||||
|
|
||||||
combineAndReturn();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Throwable t) {
|
public void onFailure(Throwable t) {
|
||||||
fail(t);
|
callback.onFailure(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a FutureCallback for the Bulletin Board Message that ties to this object
|
* 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
|
||||||
*/
|
*/
|
||||||
public FutureCallback<List<BulletinBoardMessage>> asBulletinBoardMessageListFutureCallback() {
|
class CompleteMessageReadCallback implements FutureCallback<List<BulletinBoardMessage>>{
|
||||||
return new FutureCallback<List<BulletinBoardMessage>>() {
|
|
||||||
|
private final FutureCallback<BulletinBoardMessage> callback;
|
||||||
|
|
||||||
|
public CompleteMessageReadCallback(FutureCallback<BulletinBoardMessage> callback) {
|
||||||
|
|
||||||
|
this.callback = callback;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<BulletinBoardMessage> result) {
|
public void onSuccess(List<BulletinBoardMessage> result) {
|
||||||
if (result.size() < 1){
|
if (result.size() <= 0) {
|
||||||
onFailure(new IllegalArgumentException("Server returned empty message list"));
|
onFailure(new CommunicationException("Could not find required message on the server."));
|
||||||
return;
|
} else {
|
||||||
|
|
||||||
|
BulletinBoardMessage msg = result.get(0);
|
||||||
|
|
||||||
|
if (msg.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID) {
|
||||||
|
callback.onSuccess(msg);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Create job with MAX retries for retrieval of the Batch Data List
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stub = result.get(0);
|
|
||||||
|
|
||||||
combineAndReturn();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Throwable t) {
|
public void onFailure(Throwable t) {
|
||||||
fail(t);
|
callback.onFailure(t);
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -574,9 +559,9 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readBatch(MessageID msgID, FutureCallback<BulletinBoardMessage> callback) {
|
public void readMessage(MessageID msgID, FutureCallback<BulletinBoardMessage> 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()
|
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||||
.addFilter(MessageFilter.newBuilder()
|
.addFilter(MessageFilter.newBuilder()
|
||||||
|
@ -592,39 +577,11 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
|
|
||||||
SingleServerReadMessagesWorker messageWorker = new SingleServerReadMessagesWorker(meerkatDBs.get(0), filterList, MAX_RETRIES);
|
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
|
// Submit jobs with wrapped callbacks
|
||||||
scheduleWorker(messageWorker, new RetryCallback<>(messageWorker, completeBatchReadCallback.asBulletinBoardMessageListFutureCallback()));
|
scheduleWorker(messageWorker, new RetryCallback<>(messageWorker, new CompleteMessageReadCallback(callback)));
|
||||||
scheduleWorker(batchWorker, new RetryCallback<>(batchWorker, completeBatchReadCallback.asBatchDataListFutureCallback()));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ReadBatchCallback implements FutureCallback<List<BatchChunk>> {
|
|
||||||
|
|
||||||
private final BulletinBoardMessage stub;
|
|
||||||
private final FutureCallback<BulletinBoardMessage> callback;
|
|
||||||
|
|
||||||
public ReadBatchCallback(BulletinBoardMessage stub, FutureCallback<BulletinBoardMessage> callback) {
|
|
||||||
this.stub = stub;
|
|
||||||
this.callback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuccess(List<BatchChunk> result) {
|
|
||||||
callback.onSuccess(BulletinBoardUtils.gatherBatch(stub, result));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Throwable t) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readBatchData(BulletinBoardMessage stub, FutureCallback<BulletinBoardMessage> callback) throws IllegalArgumentException{
|
public void readBatchData(BulletinBoardMessage stub, FutureCallback<BulletinBoardMessage> callback) throws IllegalArgumentException{
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
|
||||||
import com.google.protobuf.Timestamp;
|
import com.google.protobuf.Timestamp;
|
||||||
import meerkat.bulletinboard.workers.multiserver.*;
|
import meerkat.bulletinboard.workers.multiserver.*;
|
||||||
import meerkat.protobuf.BulletinBoardAPI;
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.protobuf.Crypto.Signature;
|
import meerkat.protobuf.Crypto.Signature;
|
||||||
import meerkat.protobuf.Voting.*;
|
import meerkat.protobuf.Voting.*;
|
||||||
|
@ -208,11 +207,11 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readBatch(MessageID msgID, FutureCallback<BulletinBoardMessage> callback) {
|
public void readMessage(MessageID msgID, FutureCallback<BulletinBoardMessage> callback) {
|
||||||
|
|
||||||
//Create job
|
//Create job
|
||||||
MultiServerReadBatchWorker worker =
|
MultiServerReadMessageWorker worker =
|
||||||
new MultiServerReadBatchWorker(clients, minAbsoluteRedundancy, msgID, READ_MESSAGES_RETRY_NUM, callback);
|
new MultiServerReadMessageWorker(clients, minAbsoluteRedundancy, msgID, READ_MESSAGES_RETRY_NUM, callback);
|
||||||
|
|
||||||
// Submit job
|
// Submit job
|
||||||
executorService.submit(worker);
|
executorService.submit(worker);
|
||||||
|
|
|
@ -22,7 +22,7 @@ public class MultiServerReadBatchDataWorker extends MultiServerGenericReadWorker
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRead(MessageID payload, SingleServerBulletinBoardClient client) {
|
protected void doRead(MessageID payload, SingleServerBulletinBoardClient client) {
|
||||||
client.readBatch(payload, this);
|
client.readMessage(payload, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,9 @@ import java.util.List;
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||||
*/
|
*/
|
||||||
public class MultiServerReadBatchWorker extends MultiServerGenericReadWorker<MessageID, BulletinBoardMessage> {
|
public class MultiServerReadMessageWorker extends MultiServerGenericReadWorker<MessageID, BulletinBoardMessage> {
|
||||||
|
|
||||||
public MultiServerReadBatchWorker(List<SingleServerBulletinBoardClient> clients,
|
public MultiServerReadMessageWorker(List<SingleServerBulletinBoardClient> clients,
|
||||||
int minServers, MessageID payload, int maxRetry,
|
int minServers, MessageID payload, int maxRetry,
|
||||||
FutureCallback<BulletinBoardMessage> futureCallback) {
|
FutureCallback<BulletinBoardMessage> futureCallback) {
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ public class MultiServerReadBatchWorker extends MultiServerGenericReadWorker<Mes
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRead(MessageID payload, SingleServerBulletinBoardClient client) {
|
protected void doRead(MessageID payload, SingleServerBulletinBoardClient client) {
|
||||||
client.readBatch(payload, this);
|
client.readMessage(payload, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.*;
|
||||||
|
import com.google.protobuf.Timestamp;
|
||||||
|
|
||||||
import static meerkat.bulletinboard.BulletinBoardSynchronizer.SyncStatus;
|
import static meerkat.bulletinboard.BulletinBoardSynchronizer.SyncStatus;
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ public class BulletinBoardSynchronizerTest {
|
||||||
private static String KEYFILE_PASSWORD1 = "secret";
|
private static String KEYFILE_PASSWORD1 = "secret";
|
||||||
private static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt";
|
private static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt";
|
||||||
|
|
||||||
private static GenericBatchDigitalSignature[] signers;
|
private static BulletinBoardSignature[] signers;
|
||||||
private static ByteString[] signerIDs;
|
private static ByteString[] signerIDs;
|
||||||
|
|
||||||
private Semaphore semaphore;
|
private Semaphore semaphore;
|
||||||
|
@ -69,10 +70,10 @@ public class BulletinBoardSynchronizerTest {
|
||||||
messageGenerator = new BulletinBoardMessageGenerator(new Random(0));
|
messageGenerator = new BulletinBoardMessageGenerator(new Random(0));
|
||||||
messageComparator = new BulletinBoardMessageComparator();
|
messageComparator = new BulletinBoardMessageComparator();
|
||||||
|
|
||||||
signers = new GenericBatchDigitalSignature[1];
|
signers = new BulletinBoardSignature[1];
|
||||||
signerIDs = new ByteString[1];
|
signerIDs = new ByteString[1];
|
||||||
|
|
||||||
signers[0] = new GenericBatchDigitalSignature(new ECDSASignature());
|
signers[0] = new GenericBulletinBoardSignature(new ECDSASignature());
|
||||||
signerIDs[0] = signers[0].getSignerID();
|
signerIDs[0] = signers[0].getSignerID();
|
||||||
|
|
||||||
InputStream keyStream = BulletinBoardSynchronizerTest.class.getResourceAsStream(KEYFILE_EXAMPLE);
|
InputStream keyStream = BulletinBoardSynchronizerTest.class.getResourceAsStream(KEYFILE_EXAMPLE);
|
||||||
|
@ -203,15 +204,34 @@ public class BulletinBoardSynchronizerTest {
|
||||||
@Test
|
@Test
|
||||||
public void testSync() throws SignatureException, CommunicationException, InterruptedException {
|
public void testSync() throws SignatureException, CommunicationException, InterruptedException {
|
||||||
|
|
||||||
final int BATCH_ID = 1;
|
Timestamp timestamp = Timestamp.newBuilder()
|
||||||
|
.setSeconds(15252162)
|
||||||
|
.setNanos(85914)
|
||||||
|
.build();
|
||||||
|
|
||||||
BulletinBoardMessage msg = messageGenerator.generateRandomMessage(signers, 10, 10);
|
BulletinBoardMessage msg = messageGenerator.generateRandomMessage(signers, timestamp, 10, 10);
|
||||||
|
|
||||||
MessageID msgID = localClient.postMessage(msg);
|
MessageID msgID = localClient.postMessage(msg);
|
||||||
|
|
||||||
CompleteBatch completeBatch = messageGenerator.generateRandomBatch(signers[0],BATCH_ID,10,10,10);
|
timestamp = Timestamp.newBuilder()
|
||||||
|
.setSeconds(51511653)
|
||||||
|
.setNanos(3625)
|
||||||
|
.build();
|
||||||
|
|
||||||
localClient.postBatch(completeBatch);
|
BulletinBoardMessage batchMessage = messageGenerator.generateRandomMessage(signers,timestamp, 100, 10);
|
||||||
|
|
||||||
|
MessageID batchMsgID = localClient.postAsBatch(batchMessage, 10);
|
||||||
|
|
||||||
|
BulletinBoardMessage test = localClient.readMessage(batchMsgID);
|
||||||
|
|
||||||
|
BulletinBoardMessage stub = localClient.readMessages(MessageFilterList.newBuilder()
|
||||||
|
.addFilter(MessageFilter.newBuilder()
|
||||||
|
.setType(FilterType.MSG_ID)
|
||||||
|
.setId(batchMsgID.getID())
|
||||||
|
.build())
|
||||||
|
.build()).get(0);
|
||||||
|
|
||||||
|
BulletinBoardMessage test2 = localClient.readBatchData(stub);
|
||||||
|
|
||||||
synchronizer.subscribeToSyncStatus(new SyncStatusCallback(SyncStatus.SYNCHRONIZED));
|
synchronizer.subscribeToSyncStatus(new SyncStatusCallback(SyncStatus.SYNCHRONIZED));
|
||||||
|
|
||||||
|
@ -244,19 +264,21 @@ public class BulletinBoardSynchronizerTest {
|
||||||
assertThat("Wrong number of messages returned.", msgList.size() == 1);
|
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);
|
assertThat("Returned message is not equal to original one", messageComparator.compare(msgList.get(0),msg) == 0);
|
||||||
|
|
||||||
CompleteBatch returnedBatch = remoteClient.readBatch(BatchSpecificationMessage.newBuilder()
|
BulletinBoardMessage returnedBatchMsg = remoteClient.readMessage(batchMsgID);
|
||||||
.setSignerId(signerIDs[0])
|
|
||||||
.setBatchId(BATCH_ID)
|
|
||||||
.build());
|
|
||||||
|
|
||||||
assertThat("Returned batch does not equal original one.", completeBatch.equals(returnedBatch));
|
assertThat("Returned batch does not equal original one.", messageComparator.compare(returnedBatchMsg, batchMessage) == 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testServerError() throws SignatureException, CommunicationException, InterruptedException {
|
public void testServerError() throws SignatureException, CommunicationException, InterruptedException {
|
||||||
|
|
||||||
BulletinBoardMessage msg = messageGenerator.generateRandomMessage(signers, 10, 10);
|
Timestamp timestamp = Timestamp.newBuilder()
|
||||||
|
.setSeconds(945736256)
|
||||||
|
.setNanos(276788)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
BulletinBoardMessage msg = messageGenerator.generateRandomMessage(signers, timestamp, 10, 10);
|
||||||
|
|
||||||
remoteClient.close();
|
remoteClient.close();
|
||||||
|
|
||||||
|
@ -275,17 +297,18 @@ public class BulletinBoardSynchronizerTest {
|
||||||
synchronizer.stop();
|
synchronizer.stop();
|
||||||
thread.join();
|
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
|
@After
|
||||||
public void close() {
|
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();
|
synchronizer.stop();
|
||||||
localClient.close();
|
localClient.close();
|
||||||
remoteClient.close();
|
remoteClient.close();
|
||||||
|
|
|
@ -5,9 +5,13 @@ import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.Timestamp;
|
import com.google.protobuf.Timestamp;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.crypto.concrete.ECDSASignature;
|
import meerkat.crypto.concrete.ECDSASignature;
|
||||||
|
import meerkat.crypto.concrete.SHA256Digest;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.protobuf.Crypto;
|
import meerkat.protobuf.Crypto;
|
||||||
import meerkat.util.BulletinBoardMessageComparator;
|
import meerkat.util.BulletinBoardMessageComparator;
|
||||||
|
import meerkat.util.BulletinBoardMessageGenerator;
|
||||||
|
import meerkat.util.BulletinBoardUtils;
|
||||||
|
import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -27,7 +31,7 @@ public class GenericBulletinBoardClientTester {
|
||||||
|
|
||||||
// Signature resources
|
// Signature resources
|
||||||
|
|
||||||
private GenericBatchDigitalSignature signers[];
|
private BulletinBoardSignature signers[];
|
||||||
private ByteString[] signerIDs;
|
private ByteString[] signerIDs;
|
||||||
|
|
||||||
private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
|
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 RedundancyCallback redundancyCallback;
|
||||||
private ReadCallback readCallback;
|
private ReadCallback readCallback;
|
||||||
private ReadBatchCallback readBatchCallback;
|
|
||||||
|
|
||||||
// Sync and misc
|
// Sync and misc
|
||||||
|
|
||||||
private Semaphore jobSemaphore;
|
private Semaphore jobSemaphore;
|
||||||
private Vector<Throwable> thrown;
|
private Vector<Throwable> thrown;
|
||||||
private Random random;
|
private Random random;
|
||||||
|
private BulletinBoardMessageGenerator generator;
|
||||||
|
|
||||||
|
private BulletinBoardDigest digest;
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
|
|
||||||
|
@ -62,10 +68,10 @@ public class GenericBulletinBoardClientTester {
|
||||||
|
|
||||||
this.bulletinBoardClient = bulletinBoardClient;
|
this.bulletinBoardClient = bulletinBoardClient;
|
||||||
|
|
||||||
signers = new GenericBatchDigitalSignature[2];
|
signers = new GenericBulletinBoardSignature[2];
|
||||||
signerIDs = new ByteString[signers.length];
|
signerIDs = new ByteString[signers.length];
|
||||||
signers[0] = new GenericBatchDigitalSignature(new ECDSASignature());
|
signers[0] = new GenericBulletinBoardSignature(new ECDSASignature());
|
||||||
signers[1] = new GenericBatchDigitalSignature(new ECDSASignature());
|
signers[1] = new GenericBulletinBoardSignature(new ECDSASignature());
|
||||||
|
|
||||||
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
||||||
char[] password = KEYFILE_PASSWORD1.toCharArray();
|
char[] password = KEYFILE_PASSWORD1.toCharArray();
|
||||||
|
@ -107,6 +113,10 @@ public class GenericBulletinBoardClientTester {
|
||||||
fail("Couldn't find signing key " + e.getMessage());
|
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
|
// Callback definitions
|
||||||
|
@ -137,16 +147,21 @@ public class GenericBulletinBoardClientTester {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Boolean msg) {
|
public void onSuccess(Boolean msg) {
|
||||||
|
|
||||||
System.err.println("Post operation completed");
|
System.err.println("Post operation completed");
|
||||||
jobSemaphore.release();
|
|
||||||
//TODO: Change Assert mechanism to exception one
|
|
||||||
if (isAssert) {
|
if (isAssert) {
|
||||||
if (assertValue) {
|
if (assertValue && !msg) {
|
||||||
assertThat("Post operation failed", msg, is(Boolean.TRUE));
|
genericHandleFailure(new AssertionError("Post operation failed"));
|
||||||
|
} else if (!assertValue && msg){
|
||||||
|
genericHandleFailure(new AssertionError("Post operation succeeded unexpectedly"));
|
||||||
} else {
|
} else {
|
||||||
assertThat("Post operation succeeded unexpectedly", msg, is(Boolean.FALSE));
|
jobSemaphore.release();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
jobSemaphore.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -209,21 +224,24 @@ public class GenericBulletinBoardClientTester {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ReadBatchCallback implements FutureCallback<CompleteBatch> {
|
private class ReadBatchCallback implements FutureCallback<BulletinBoardMessage>{
|
||||||
|
|
||||||
private CompleteBatch expectedBatch;
|
private BulletinBoardMessage expectedMsg;
|
||||||
|
|
||||||
public ReadBatchCallback(CompleteBatch expectedBatch) {
|
public ReadBatchCallback(BulletinBoardMessage expectedMsg) {
|
||||||
this.expectedBatch = expectedBatch;
|
this.expectedMsg = expectedMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(CompleteBatch batch) {
|
public void onSuccess(BulletinBoardMessage msg) {
|
||||||
|
|
||||||
System.err.println(batch);
|
BulletinBoardMessageComparator msgComparator = new BulletinBoardMessageComparator();
|
||||||
|
|
||||||
|
if (msgComparator.compare(msg, expectedMsg) != 0) {
|
||||||
|
genericHandleFailure(new AssertionError("Batch read returned different message.\nExpected:" + expectedMsg + "\nRecieved:" + msg + "\n"));
|
||||||
|
} else {
|
||||||
jobSemaphore.release();
|
jobSemaphore.release();
|
||||||
|
}
|
||||||
assertThat("Batch returned is incorrect", batch, is(equalTo(expectedBatch)));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
// Test methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -310,7 +275,13 @@ public class GenericBulletinBoardClientTester {
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
||||||
if (thrown.size() > 0) {
|
if (thrown.size() > 0) {
|
||||||
|
|
||||||
|
for (Throwable t : thrown){
|
||||||
|
System.err.println(t.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
assert false;
|
assert false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -394,59 +365,54 @@ public class GenericBulletinBoardClientTester {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests posting a batch by parts
|
* Tests posting a batch by parts
|
||||||
* Also tests not being able to post to a closed batch
|
|
||||||
* @throws CommunicationException, SignatureException, InterruptedException
|
* @throws CommunicationException, SignatureException, InterruptedException
|
||||||
*/
|
*/
|
||||||
public void testBatchPost() 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 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
|
// Begin batch
|
||||||
|
|
||||||
bulletinBoardClient.beginBatch(completeBatch.getBeginBatchMessage(), postCallback);
|
bulletinBoardClient.beginBatch(msg.getMsg().getTagList(), new FutureCallback<BatchIdentifier>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final BatchIdentifier identifier) {
|
||||||
|
|
||||||
|
bulletinBoardClient.postBatchData(identifier, BulletinBoardUtils.breakToBatch(msg, CHUNK_SIZE), new FutureCallback<Boolean>() {
|
||||||
|
|
||||||
|
@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();
|
jobSemaphore.acquire();
|
||||||
|
|
||||||
// Post data
|
digest.reset();
|
||||||
|
digest.update(msg);
|
||||||
|
|
||||||
bulletinBoardClient.postBatchData(signerIDs[SIGNER], BATCH_ID, completeBatch.getBatchDataList(), postCallback);
|
bulletinBoardClient.readMessage(digest.digestAsMessageID(), new ReadBatchCallback(msg));
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
jobSemaphore.acquire();
|
jobSemaphore.acquire();
|
||||||
|
|
||||||
|
@ -454,62 +420,56 @@ public class GenericBulletinBoardClientTester {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Posts a complete batch message
|
* Posts a complete batch message
|
||||||
* Checks reading of the message
|
* Checks reading of the message in two parts
|
||||||
* @throws CommunicationException, SignatureException, InterruptedException
|
* @throws CommunicationException, SignatureException, InterruptedException
|
||||||
*/
|
*/
|
||||||
public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException {
|
public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException {
|
||||||
|
|
||||||
final int SIGNER = 0;
|
final int BATCH_LENGTH = 100;
|
||||||
final int BATCH_ID = 101;
|
final int CHUNK_SIZE = 99;
|
||||||
final int BATCH_LENGTH = 50;
|
final int TAG_NUM = 8;
|
||||||
|
|
||||||
|
final BulletinBoardMessage msg = generator.generateRandomMessage(signers, BATCH_LENGTH, TAG_NUM);
|
||||||
|
|
||||||
// Post batch
|
// Post batch
|
||||||
|
|
||||||
CompleteBatch completeBatch = createRandomBatch(SIGNER, BATCH_ID, BATCH_LENGTH);
|
MessageID msgID = bulletinBoardClient.postAsBatch(msg, CHUNK_SIZE, postCallback);
|
||||||
|
|
||||||
bulletinBoardClient.postBatch(completeBatch,postCallback);
|
|
||||||
|
|
||||||
jobSemaphore.acquire();
|
jobSemaphore.acquire();
|
||||||
|
|
||||||
// Read batch
|
// Read batch
|
||||||
|
|
||||||
BatchSpecificationMessage batchSpecificationMessage =
|
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||||
BatchSpecificationMessage.newBuilder()
|
.addFilter(MessageFilter.newBuilder()
|
||||||
.setSignerId(signerIDs[SIGNER])
|
.setType(FilterType.MSG_ID)
|
||||||
.setBatchId(BATCH_ID)
|
.setId(msgID.getID())
|
||||||
.setStartPosition(0)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
readBatchCallback = new ReadBatchCallback(completeBatch);
|
|
||||||
|
|
||||||
bulletinBoardClient.readBatch(batchSpecificationMessage, readBatchCallback);
|
|
||||||
|
|
||||||
jobSemaphore.acquire();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests that an unopened batch cannot be closed
|
|
||||||
* @throws CommunicationException, InterruptedException
|
|
||||||
*/
|
|
||||||
public void testInvalidBatchClose() throws CommunicationException, InterruptedException {
|
|
||||||
|
|
||||||
final int NON_EXISTENT_BATCH_ID = 999;
|
|
||||||
|
|
||||||
CloseBatchMessage closeBatchMessage =
|
|
||||||
CloseBatchMessage.newBuilder()
|
|
||||||
.setBatchId(NON_EXISTENT_BATCH_ID)
|
|
||||||
.setBatchLength(1)
|
|
||||||
.setSig(Crypto.Signature.getDefaultInstance())
|
|
||||||
.setTimestamp(Timestamp.newBuilder()
|
|
||||||
.setSeconds(9)
|
|
||||||
.setNanos(12)
|
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Try to stop the (unopened) batch;
|
bulletinBoardClient.readMessages(filterList, new FutureCallback<List<BulletinBoardMessage>>() {
|
||||||
|
|
||||||
bulletinBoardClient.closeBatch(closeBatchMessage, failPostCallback);
|
@Override
|
||||||
|
public void onSuccess(List<BulletinBoardMessage> msgList) {
|
||||||
|
|
||||||
|
if (msgList.size() != 1) {
|
||||||
|
|
||||||
|
genericHandleFailure(new AssertionError("Wrong number of stubs returned. Expected: 1; Found: " + msgList.size()));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
BulletinBoardMessage retrievedMsg = msgList.get(0);
|
||||||
|
bulletinBoardClient.readBatchData(retrievedMsg, new ReadBatchCallback(msg));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable t) {
|
||||||
|
genericHandleFailure(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
jobSemaphore.acquire();
|
jobSemaphore.acquire();
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import static org.junit.Assert.fail;
|
||||||
*/
|
*/
|
||||||
public class GenericSubscriptionClientTester {
|
public class GenericSubscriptionClientTester {
|
||||||
|
|
||||||
private GenericBatchDigitalSignature signers[];
|
private BulletinBoardSignature signers[];
|
||||||
private ByteString[] signerIDs;
|
private ByteString[] signerIDs;
|
||||||
|
|
||||||
private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
|
private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
|
||||||
|
@ -47,10 +47,10 @@ public class GenericSubscriptionClientTester {
|
||||||
|
|
||||||
this.bulletinBoardClient = bulletinBoardClient;
|
this.bulletinBoardClient = bulletinBoardClient;
|
||||||
|
|
||||||
signers = new GenericBatchDigitalSignature[2];
|
signers = new BulletinBoardSignature[2];
|
||||||
signerIDs = new ByteString[signers.length];
|
signerIDs = new ByteString[signers.length];
|
||||||
signers[0] = new GenericBatchDigitalSignature(new ECDSASignature());
|
signers[0] = new GenericBulletinBoardSignature(new ECDSASignature());
|
||||||
signers[1] = new GenericBatchDigitalSignature(new ECDSASignature());
|
signers[1] = new GenericBulletinBoardSignature(new ECDSASignature());
|
||||||
|
|
||||||
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
||||||
char[] password = KEYFILE_PASSWORD1.toCharArray();
|
char[] password = KEYFILE_PASSWORD1.toCharArray();
|
||||||
|
|
|
@ -100,13 +100,6 @@ public class LocalBulletinBoardClientTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInvalidBatchClose() throws CommunicationException, InterruptedException {
|
|
||||||
|
|
||||||
clientTest.testInvalidBatchClose();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSubscription() throws SignatureException, CommunicationException {
|
public void testSubscription() throws SignatureException, CommunicationException {
|
||||||
subscriptionTester.init();
|
subscriptionTester.init();
|
||||||
|
|
|
@ -85,11 +85,4 @@ public class ThreadedBulletinBoardClientIntegrationTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInvalidBatchClose() throws CommunicationException, InterruptedException {
|
|
||||||
|
|
||||||
clientTest.testInvalidBatchClose();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import meerkat.crypto.DigitalSignature;
|
||||||
import meerkat.crypto.concrete.SHA256Digest;
|
import meerkat.crypto.concrete.SHA256Digest;
|
||||||
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
import meerkat.protobuf.Comm;
|
||||||
import meerkat.protobuf.Crypto.Signature;
|
import meerkat.protobuf.Crypto.Signature;
|
||||||
import meerkat.protobuf.Crypto.SignatureVerificationKey;
|
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
|
@Override
|
||||||
public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||||
|
|
||||||
|
checkConnection();
|
||||||
|
|
||||||
// Perform a post, calculate the message ID and check the signature for authenticity
|
// Perform a post, calculate the message ID and check the signature for authenticity
|
||||||
if (postMessage(msg, null) != -1){
|
if (postMessage(msg, null) != -1){
|
||||||
return BoolValue.newBuilder().setValue(true).build(); // Message was posted
|
return BoolValue.newBuilder().setValue(true).build(); // Message was posted
|
||||||
|
@ -561,6 +570,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
@Override
|
@Override
|
||||||
public BoolValue deleteMessage(MessageID msgID) throws CommunicationException {
|
public BoolValue deleteMessage(MessageID msgID) throws CommunicationException {
|
||||||
|
|
||||||
|
checkConnection();
|
||||||
|
|
||||||
String sql = sqlQueryProvider.getSQLString(QueryType.DELETE_MSG_BY_ID);
|
String sql = sqlQueryProvider.getSQLString(QueryType.DELETE_MSG_BY_ID);
|
||||||
Map namedParameters = new HashMap();
|
Map namedParameters = new HashMap();
|
||||||
|
|
||||||
|
@ -577,6 +588,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
@Override
|
@Override
|
||||||
public BoolValue deleteMessage(long entryNum) throws CommunicationException {
|
public BoolValue deleteMessage(long entryNum) throws CommunicationException {
|
||||||
|
|
||||||
|
checkConnection();
|
||||||
|
|
||||||
String sql = sqlQueryProvider.getSQLString(QueryType.DELETE_MSG_BY_ENTRY);
|
String sql = sqlQueryProvider.getSQLString(QueryType.DELETE_MSG_BY_ENTRY);
|
||||||
Map namedParameters = new HashMap();
|
Map namedParameters = new HashMap();
|
||||||
|
|
||||||
|
@ -702,6 +715,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
@Override
|
@Override
|
||||||
public void readMessages(MessageFilterList filterList, MessageOutputStream<BulletinBoardMessage> out) throws CommunicationException {
|
public void readMessages(MessageFilterList filterList, MessageOutputStream<BulletinBoardMessage> out) throws CommunicationException {
|
||||||
|
|
||||||
|
checkConnection();
|
||||||
|
|
||||||
BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder();
|
BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder();
|
||||||
|
|
||||||
// SQL length is roughly 50 characters per filter + 50 for the query itself
|
// SQL length is roughly 50 characters per filter + 50 for the query itself
|
||||||
|
@ -725,6 +740,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
@Override
|
@Override
|
||||||
public Int32Value getMessageCount(MessageFilterList filterList) throws CommunicationException {
|
public Int32Value getMessageCount(MessageFilterList filterList) throws CommunicationException {
|
||||||
|
|
||||||
|
checkConnection();
|
||||||
|
|
||||||
BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder();
|
BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder();
|
||||||
|
|
||||||
// SQL length is roughly 50 characters per filter + 50 for the query itself
|
// SQL length is roughly 50 characters per filter + 50 for the query itself
|
||||||
|
@ -793,6 +810,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
@Override
|
@Override
|
||||||
public Int64Value beginBatch(BeginBatchMessage message) throws CommunicationException {
|
public Int64Value beginBatch(BeginBatchMessage message) throws CommunicationException {
|
||||||
|
|
||||||
|
checkConnection();
|
||||||
|
|
||||||
// Store tags
|
// Store tags
|
||||||
String sql = sqlQueryProvider.getSQLString(QueryType.STORE_BATCH_TAGS);
|
String sql = sqlQueryProvider.getSQLString(QueryType.STORE_BATCH_TAGS);
|
||||||
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
|
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
|
||||||
|
@ -814,6 +833,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
@Override
|
@Override
|
||||||
public BoolValue postBatchMessage(BatchMessage batchMessage) throws CommunicationException{
|
public BoolValue postBatchMessage(BatchMessage batchMessage) throws CommunicationException{
|
||||||
|
|
||||||
|
checkConnection();
|
||||||
|
|
||||||
// Make sure batch is open
|
// Make sure batch is open
|
||||||
if (!isBatchOpen(batchMessage.getBatchId())) {
|
if (!isBatchOpen(batchMessage.getBatchId())) {
|
||||||
return BoolValue.newBuilder().setValue(false).build();
|
return BoolValue.newBuilder().setValue(false).build();
|
||||||
|
@ -837,6 +858,7 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
@Override
|
@Override
|
||||||
public BoolValue closeBatch(CloseBatchMessage message) throws CommunicationException {
|
public BoolValue closeBatch(CloseBatchMessage message) throws CommunicationException {
|
||||||
|
|
||||||
|
checkConnection();
|
||||||
|
|
||||||
// Check batch size
|
// Check batch size
|
||||||
|
|
||||||
|
@ -916,6 +938,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
@Override
|
@Override
|
||||||
public void readBatch(BatchQuery batchQuery, MessageOutputStream<BatchChunk> out) throws CommunicationException, IllegalArgumentException{
|
public void readBatch(BatchQuery batchQuery, MessageOutputStream<BatchChunk> out) throws CommunicationException, IllegalArgumentException{
|
||||||
|
|
||||||
|
checkConnection();
|
||||||
|
|
||||||
// Check that batch is closed
|
// Check that batch is closed
|
||||||
if (!isBatchClosed(batchQuery.getMsgID())) {
|
if (!isBatchClosed(batchQuery.getMsgID())) {
|
||||||
throw new IllegalArgumentException("No such batch");
|
throw new IllegalArgumentException("No such batch");
|
||||||
|
@ -950,7 +974,9 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) {
|
public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException{
|
||||||
|
|
||||||
|
checkConnection();
|
||||||
|
|
||||||
if (generateSyncQueryParams == null
|
if (generateSyncQueryParams == null
|
||||||
|| !generateSyncQueryParams.hasFilterList()
|
|| !generateSyncQueryParams.hasFilterList()
|
||||||
|
@ -1029,6 +1055,8 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
@Override
|
@Override
|
||||||
public SyncQueryResponse querySync(SyncQuery syncQuery) throws CommunicationException {
|
public SyncQueryResponse querySync(SyncQuery syncQuery) throws CommunicationException {
|
||||||
|
|
||||||
|
checkConnection();
|
||||||
|
|
||||||
if (syncQuery == null){
|
if (syncQuery == null){
|
||||||
return SyncQueryResponse.newBuilder()
|
return SyncQueryResponse.newBuilder()
|
||||||
.setLastEntryNum(-1)
|
.setLastEntryNum(-1)
|
||||||
|
|
|
@ -87,19 +87,20 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient {
|
||||||
* Read all messages posted matching the given filter in an asynchronous manner
|
* 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
|
* 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
|
* 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.
|
* 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
|
* @param callback is a callback function class for handling results of the operation
|
||||||
*/
|
*/
|
||||||
public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback);
|
public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a given batch message from the bulletin board
|
* Read a given message from the bulletin board
|
||||||
* @param msgID is the batch message ID to be read
|
* 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
|
* @param callback is a callback class for handling the result of the operation
|
||||||
*/
|
*/
|
||||||
public void readBatch(MessageID msgID, FutureCallback<BulletinBoardMessage> callback);
|
public void readMessage(MessageID msgID, FutureCallback<BulletinBoardMessage> callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read batch data for a specific stub message
|
* Read batch data for a specific stub message
|
||||||
|
|
|
@ -55,12 +55,13 @@ public interface BulletinBoardClient {
|
||||||
MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException;
|
MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a given batch message from the bulletin board
|
* Read a given message from the bulletin board
|
||||||
* @param msgID is the batch message ID to be read
|
* If the message is a batch: returns a complete message containing the batch data as well as the metadata
|
||||||
* @return the complete batch
|
* @param msgID is the ID of the message to be read
|
||||||
|
* @return the complete message
|
||||||
* @throws CommunicationException if operation is unsuccessful
|
* @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
|
* Read batch data for a specific stub message
|
||||||
|
|
Loading…
Reference in New Issue