parent
4c33e923b2
commit
7c60e487cc
|
@ -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);
|
||||
|
|
|
@ -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<Boolean> {
|
||||
|
||||
private final long entryNum;
|
||||
private final FutureCallback<Void> callback;
|
||||
|
||||
public MessageDeleteCallback(long entryNum) {
|
||||
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) {
|
||||
// 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<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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -52,6 +105,20 @@ public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronize
|
|||
@Override
|
||||
public void onSuccess(List<BulletinBoardMessage> result) {
|
||||
|
||||
// Notify Message Count callbacks if needed
|
||||
|
||||
if (syncStatus != SyncStatus.SYNCHRONIZED || result.size() > 0) {
|
||||
|
||||
for (FutureCallback<Integer> 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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Throwable> 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<SyncStatus> {
|
||||
|
||||
@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<Integer> {
|
||||
|
||||
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<BulletinBoardMessage> 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();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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)");
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String> randomStrings(int length) {
|
||||
|
||||
List<String> 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<String> tags) throws SignatureException {
|
||||
|
||||
CompleteBatch result = new CompleteBatch(BeginBatchMessage.newBuilder()
|
||||
.setSignerId(signer.getSignerID())
|
||||
.setBatchId(batchId)
|
||||
.addAllTag(tags)
|
||||
.addAllTag(randomStrings(tagCount))
|
||||
.build());
|
||||
|
||||
List<BatchData> 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<String>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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<String> 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<String>());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue