diff --git a/.gitignore b/.gitignore index 6c07527..3a97743 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,18 @@ -.gradle -.idea -build -bin -.settings -.classpath -.project -out -*.iml -*.ipr -*.iws -**/*.swp -*.prefs -*.project -*.classpath -bulletin-board-server/local-instances/meerkat.db +.gradle +.idea +build +bin +.settings +.classpath +.project +out +*.iml +*.ipr +*.iws +**/*.swp +*.prefs +*.project +*.classpath +*.db +*.sql +.arcconfig diff --git a/build.gradle b/build.gradle index e3f1a0c..9f070e3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,10 @@ - -subprojects { proj -> - proj.afterEvaluate { - // Used to generate initial maven-dir layout - task "create-dirs" { description = "Create default maven directory structure" } << { - sourceSets*.java.srcDirs*.each { it.mkdirs() } - sourceSets*.resources.srcDirs*.each { it.mkdirs() } - } - } -} + +subprojects { proj -> + proj.afterEvaluate { + // Used to generate initial maven-dir layout + task "create-dirs" { description = "Create default maven directory structure" } << { + sourceSets*.java.srcDirs*.each { it.mkdirs() } + sourceSets*.resources.srcDirs*.each { it.mkdirs() } + } + } +} diff --git a/build.gradle-template b/build.gradle-template index 074667f..60ec7c9 100644 --- a/build.gradle-template +++ b/build.gradle-template @@ -1,220 +1,220 @@ - -plugins { - id "us.kirchmeier.capsule" version "1.0.1" - id 'com.google.protobuf' version '0.7.0' -} - -apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'idea' - -apply plugin: 'maven-publish' - -// Uncomment the lines below to define an application -// (this will also allow you to build a "fatCapsule" which includes -// the entire application, including all dependencies in a single jar) -//apply plugin: 'application' -//mainClassName='your.main.ApplicationClass' - -// Is this a snapshot version? -ext { isSnapshot = false } - -ext { - groupId = 'org.factcenter.meerkat' - nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" - - // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) - // Should be set in ${HOME}/.gradle/gradle.properties - nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" - nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" -} - -description = "TODO: Add a description" - -// Your project version -version = "0.0" - -version += "${isSnapshot ? '-SNAPSHOT' : ''}" - - -dependencies { - // Meerkat common - compile project(':meerkat-common') - - // Logging - compile 'org.slf4j:slf4j-api:1.7.7' - runtime 'ch.qos.logback:logback-classic:1.1.2' - runtime 'ch.qos.logback:logback-core:1.1.2' - - // Google protobufs - compile 'com.google.protobuf:protobuf-java:3.+' - - testCompile 'junit:junit:4.+' - - runtime 'org.codehaus.groovy:groovy:2.4.+' -} - - -/*==== You probably don't have to edit below this line =======*/ - -// Setup test configuration that can appear as a dependency in -// other subprojects -configurations { - testOutput.extendsFrom (testCompile) -} - -task testJar(type: Jar, dependsOn: testClasses) { - classifier = 'tests' - from sourceSets.test.output -} - -artifacts { - testOutput testJar -} - -// The run task added by the application plugin -// is also of type JavaExec. -tasks.withType(JavaExec) { - // Assign all Java system properties from - // the command line to the JavaExec task. - systemProperties System.properties -} - - -protobuf { - // Configure the protoc executable - protoc { - // Download from repositories - artifact = 'com.google.protobuf:protoc:3.+' - } -} - - -idea { - module { - project.sourceSets.each { sourceSet -> - - def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" - - println "Adding $srcDir" - // add protobuf generated sources to generated source dir. - if ("test".equals(sourceSet.name)) { - testSourceDirs += file(srcDir) - } else { - sourceDirs += file(srcDir) - } - generatedSourceDirs += file(srcDir) - - } - - // Don't exclude build directory - excludeDirs -= file(buildDir) - } -} - - -/*=================================== - * "Fat" Build targets - *===================================*/ - - -if (project.hasProperty('mainClassName') && (mainClassName != null)) { - - task mavenCapsule(type: MavenCapsule) { - description = "Generate a capsule jar that automatically downloads and caches dependencies when run." - applicationClass mainClassName - destinationDir = buildDir - } - - task fatCapsule(type: FatCapsule) { - description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class" - - destinationDir = buildDir - - def fatMain = hasProperty('fatmain') ? fatmain : mainClassName - - applicationClass fatMain - - def testJar = hasProperty('test') - - if (hasProperty('fatmain')) { - appendix = "fat-${fatMain}" - } else { - appendix = "fat" - } - - if (testJar) { - from sourceSets.test.output - } - } -} - - -/*=================================== - * Repositories - *===================================*/ - -repositories { - - // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) - maven { - url nexusRepository - - if (isSnapshot) { - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } - - // Use local maven repository - mavenLocal() - - // Use 'maven central' for other dependencies. - mavenCentral() -} - -task "info" << { - println "Project: ${project.name}" -println "Description: ${project.description}" - println "--------------------------" - println "GroupId: $groupId" - println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" - println "" -} -info.description 'Print some information about project parameters' - - -/*=================================== - * Publishing - *===================================*/ - -publishing { - publications { - mavenJava(MavenPublication) { - groupId project.groupId - pom.withXml { - asNode().appendNode('description', project.description) - } - from project.components.java - - } - } - repositories { - maven { - url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } -} - - - + +plugins { + id "us.kirchmeier.capsule" version "1.0.1" + id 'com.google.protobuf' version '0.7.0' +} + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'idea' + +apply plugin: 'maven-publish' + +// Uncomment the lines below to define an application +// (this will also allow you to build a "fatCapsule" which includes +// the entire application, including all dependencies in a single jar) +//apply plugin: 'application' +//mainClassName='your.main.ApplicationClass' + +// Is this a snapshot version? +ext { isSnapshot = false } + +ext { + groupId = 'org.factcenter.meerkat' + nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" + + // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) + // Should be set in ${HOME}/.gradle/gradle.properties + nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" + nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" +} + +description = "TODO: Add a description" + +// Your project version +version = "0.0" + +version += "${isSnapshot ? '-SNAPSHOT' : ''}" + + +dependencies { + // Meerkat common + compile project(':meerkat-common') + + // Logging + compile 'org.slf4j:slf4j-api:1.7.7' + runtime 'ch.qos.logback:logback-classic:1.1.2' + runtime 'ch.qos.logback:logback-core:1.1.2' + + // Google protobufs + compile 'com.google.protobuf:protobuf-java:3.+' + + testCompile 'junit:junit:4.+' + + runtime 'org.codehaus.groovy:groovy:2.4.+' +} + + +/*==== You probably don't have to edit below this line =======*/ + +// Setup test configuration that can appear as a dependency in +// other subprojects +configurations { + testOutput.extendsFrom (testCompile) +} + +task testJar(type: Jar, dependsOn: testClasses) { + classifier = 'tests' + from sourceSets.test.output +} + +artifacts { + testOutput testJar +} + +// The run task added by the application plugin +// is also of type JavaExec. +tasks.withType(JavaExec) { + // Assign all Java system properties from + // the command line to the JavaExec task. + systemProperties System.properties +} + + +protobuf { + // Configure the protoc executable + protoc { + // Download from repositories + artifact = 'com.google.protobuf:protoc:3.+' + } +} + + +idea { + module { + project.sourceSets.each { sourceSet -> + + def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" + + println "Adding $srcDir" + // add protobuf generated sources to generated source dir. + if ("test".equals(sourceSet.name)) { + testSourceDirs += file(srcDir) + } else { + sourceDirs += file(srcDir) + } + generatedSourceDirs += file(srcDir) + + } + + // Don't exclude build directory + excludeDirs -= file(buildDir) + } +} + + +/*=================================== + * "Fat" Build targets + *===================================*/ + + +if (project.hasProperty('mainClassName') && (mainClassName != null)) { + + task mavenCapsule(type: MavenCapsule) { + description = "Generate a capsule jar that automatically downloads and caches dependencies when run." + applicationClass mainClassName + destinationDir = buildDir + } + + task fatCapsule(type: FatCapsule) { + description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class" + + destinationDir = buildDir + + def fatMain = hasProperty('fatmain') ? fatmain : mainClassName + + applicationClass fatMain + + def testJar = hasProperty('test') + + if (hasProperty('fatmain')) { + appendix = "fat-${fatMain}" + } else { + appendix = "fat" + } + + if (testJar) { + from sourceSets.test.output + } + } +} + + +/*=================================== + * Repositories + *===================================*/ + +repositories { + + // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) + maven { + url nexusRepository + + if (isSnapshot) { + credentials { username + password + + username nexusUser + password nexusPassword + } + } + } + + // Use local maven repository + mavenLocal() + + // Use 'maven central' for other dependencies. + mavenCentral() +} + +task "info" << { + println "Project: ${project.name}" +println "Description: ${project.description}" + println "--------------------------" + println "GroupId: $groupId" + println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" + println "" +} +info.description 'Print some information about project parameters' + + +/*=================================== + * Publishing + *===================================*/ + +publishing { + publications { + mavenJava(MavenPublication) { + groupId project.groupId + pom.withXml { + asNode().appendNode('description', project.description) + } + from project.components.java + + } + } + repositories { + maven { + url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + credentials { username + password + + username nexusUser + password nexusPassword + } + } + } +} + + + diff --git a/bulletin-board-client/build.gradle b/bulletin-board-client/build.gradle index 8fdeba0..03c395a 100644 --- a/bulletin-board-client/build.gradle +++ b/bulletin-board-client/build.gradle @@ -69,7 +69,7 @@ dependencies { test { exclude '**/*IntegrationTest*' - outputs.upToDateWhen { false } +// outputs.upToDateWhen { false } } task integrationTest(type: Test) { diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/BatchDataContainer.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/BatchDataContainer.java index 53f4870..1026529 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/BatchDataContainer.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/BatchDataContainer.java @@ -1,7 +1,7 @@ package meerkat.bulletinboard; -import com.google.protobuf.ByteString; -import meerkat.protobuf.BulletinBoardAPI.BatchData; +import meerkat.protobuf.BulletinBoardAPI.BatchChunk; +import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; import java.util.List; @@ -11,15 +11,13 @@ import java.util.List; */ public class BatchDataContainer { - public final byte[] signerId; - public final int batchId; - public final List batchDataList; + public final MultiServerBatchIdentifier batchId; + public final List batchChunkList; public final int startPosition; - public BatchDataContainer(byte[] signerId, int batchId, List batchDataList, int startPosition) { - this.signerId = signerId; + public BatchDataContainer(MultiServerBatchIdentifier batchId, List batchChunkList, int startPosition) { this.batchId = batchId; - this.batchDataList = batchDataList; + this.batchChunkList = batchChunkList; this.startPosition = startPosition; } } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/BulletinClientWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/BulletinClientWorker.java index 52ac7cb..1a4b62f 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/BulletinClientWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/BulletinClientWorker.java @@ -1,38 +1,38 @@ -package meerkat.bulletinboard; - -/** - * Created by Arbel Deutsch Peled on 09-Dec-15. - * - * This class handles bulletin client work. - * It is meant to be used in a multi-threaded environment. - */ -public abstract class BulletinClientWorker { - - protected final IN payload; // Payload of the job - - private int maxRetry; // Number of retries for this job; set to -1 for infinite retries - - public BulletinClientWorker(IN payload, int maxRetry) { - this.payload = payload; - this.maxRetry = maxRetry; - } - - public IN getPayload() { - return payload; - } - - public int getMaxRetry() { - return maxRetry; - } - - public void decMaxRetry(){ - if (maxRetry > 0) { - maxRetry--; - } - } - - public boolean isRetry(){ - return (maxRetry != 0); - } - -} +package meerkat.bulletinboard; + +/** + * Created by Arbel Deutsch Peled on 09-Dec-15. + * + * This class handles bulletin client work. + * It is meant to be used in a multi-threaded environment. + */ +public abstract class BulletinClientWorker { + + protected final IN payload; // Payload of the job + + private int maxRetry; // Number of retries for this job; set to -1 for infinite retries + + public BulletinClientWorker(IN payload, int maxRetry) { + this.payload = payload; + this.maxRetry = maxRetry; + } + + public IN getPayload() { + return payload; + } + + public int getMaxRetry() { + return maxRetry; + } + + public void decMaxRetry(){ + if (maxRetry > 0) { + maxRetry--; + } + } + + public boolean isRetry(){ + return (maxRetry != 0); + } + +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java index 96ba76d..cdb86cb 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedBulletinBoardClient.java @@ -1,168 +1,490 @@ package meerkat.bulletinboard; import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.ListeningScheduledExecutorService; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.protobuf.ByteString; +import com.google.protobuf.Timestamp; import meerkat.comm.CommunicationException; -import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Crypto.Signature; import meerkat.protobuf.Voting.*; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.concurrent.Executors; /** * Created by Arbel Deutsch Peled on 03-Mar-16. * This is a full-fledged implementation of a Bulletin Board Client * It provides asynchronous access to several remote servers, as well as a local cache - * Read/write operations are performed on the local server + * Read operations are performed on the local server + * Batch reads are performed on the local server and, if they fail, also on the remote servers + * Write operations are performed on the local server + * A Synchronizer is employed in order to keep the remote server up to date * After any read is carried out, a subscription is made for the specific query to make sure the local DB will be updated * The database also employs a synchronizer which makes sure local data is sent to the remote servers */ -public class CachedBulletinBoardClient implements SubscriptionAsyncBulletinBoardClient { +public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClient { - private final BulletinBoardClient localClient; - private AsyncBulletinBoardClient remoteClient; - private BulletinBoardSubscriber subscriber; + private final AsyncBulletinBoardClient localClient; + private final AsyncBulletinBoardClient remoteClient; + private final AsyncBulletinBoardClient queueClient; + private final BulletinBoardSubscriber subscriber; + private final BulletinBoardSynchronizer synchronizer; - private final int threadPoolSize; - private final long failDelayInMilliseconds; - private final long subscriptionIntervalInMilliseconds; + private Thread syncThread; - public CachedBulletinBoardClient(BulletinBoardClient localClient, - int threadPoolSize, - long failDelayInMilliseconds, - long subscriptionIntervalInMilliseconds) - throws IllegalAccessException, InstantiationException { + private final static int DEFAULT_WAIT_CAP = 3000; + private final static int DEFAULT_SLEEP_INTERVAL = 3000; + + private class SubscriptionStoreCallback implements FutureCallback> { + + private final FutureCallback> callback; + + public SubscriptionStoreCallback(){ + callback = null; + } + + public SubscriptionStoreCallback(FutureCallback> callback){ + this.callback = callback; + } + + @Override + public void onSuccess(List result) { + for (BulletinBoardMessage msg : result) { + try { + + if (msg.getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { + + // This is a batch message: need to upload batch data as well as the message itself + BulletinBoardMessage completeMessage = localClient.readBatchData(msg); + + localClient.postMessage(completeMessage); + + } else { + + // This is a regular message: post it + localClient.postMessage(msg); + + } + + } catch (CommunicationException ignored) { + // TODO: log + } + } + } + + @Override + public void onFailure(Throwable t) { + if (callback != null) { + callback.onFailure(t); // This is some hard error that cannot be dealt with + } + } + + } + + /** + * Creates a Cached Client + * Assumes all parameters are initialized + * @param localClient is a Client for the local instance + * @param remoteClient is a Client for the remote instance(s); Should have endless retries for post operations + * @param subscriber is a subscription service to the remote instance(s) + * @param queueClient is a client for a local deletable server to be used as a queue for not-yet-uploaded messages + */ + public CachedBulletinBoardClient(AsyncBulletinBoardClient localClient, + AsyncBulletinBoardClient remoteClient, + BulletinBoardSubscriber subscriber, + DeletableSubscriptionBulletinBoardClient queueClient, + int sleepInterval, + int waitCap) { this.localClient = localClient; - this.threadPoolSize = threadPoolSize; - this.failDelayInMilliseconds = failDelayInMilliseconds; - this.subscriptionIntervalInMilliseconds = subscriptionIntervalInMilliseconds; + this.remoteClient = remoteClient; + this.subscriber = subscriber; + this.queueClient = queueClient; - remoteClient = new ThreadedBulletinBoardClient(); + this.synchronizer = new SimpleBulletinBoardSynchronizer(sleepInterval,waitCap); + synchronizer.init(queueClient, remoteClient); + syncThread = new Thread(synchronizer); + syncThread.start(); + } + + /** + * Creates a Cached Client + * Used default values foe the time caps + * */ + public CachedBulletinBoardClient(AsyncBulletinBoardClient localClient, + AsyncBulletinBoardClient remoteClient, + BulletinBoardSubscriber subscriber, + DeletableSubscriptionBulletinBoardClient queue) { + + this(localClient, remoteClient, subscriber, queue, DEFAULT_SLEEP_INTERVAL, DEFAULT_WAIT_CAP); + } + + @Override + public MessageID postMessage(final BulletinBoardMessage msg, final FutureCallback callback) { + + return localClient.postMessage(msg, new FutureCallback() { + @Override + public void onSuccess(Boolean result) { + remoteClient.postMessage(msg, callback); + } + + @Override + public void onFailure(Throwable t) { + if (callback != null) + callback.onFailure(t); + } + }); } @Override - public MessageID postMessage(BulletinBoardMessage msg, FutureCallback callback) { - return null; - } + public MessageID postAsBatch(final BulletinBoardMessage msg, final int chunkSize, final FutureCallback callback) { - @Override - public MessageID postBatch(CompleteBatch completeBatch, FutureCallback callback) { - return null; - } + return localClient.postAsBatch(msg, chunkSize, new FutureCallback() { + @Override + public void onSuccess(Boolean result) { + remoteClient.postAsBatch(msg, chunkSize, callback); + } - @Override - public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback callback) { + @Override + public void onFailure(Throwable t) { + if (callback != null) + callback.onFailure(t); + } + }); } @Override - public void postBatchData(byte[] signerId, int batchId, List batchDataList, int startPosition, FutureCallback callback) { + public void beginBatch(final Iterable tags, final FutureCallback callback) { + + localClient.beginBatch(tags, new FutureCallback() { + + private BatchIdentifier localIdentifier; + + @Override + public void onSuccess(BatchIdentifier result) { + + localIdentifier = result; + + remoteClient.beginBatch(tags, new FutureCallback() { + @Override + public void onSuccess(BatchIdentifier result) { + if (callback != null) + callback.onSuccess(new CachedClientBatchIdentifier(localIdentifier, result)); + } + + @Override + public void onFailure(Throwable t) { + if (callback != null) + callback.onFailure(t); + } + }); + + } + + @Override + public void onFailure(Throwable t) { + if (callback != null) + callback.onFailure(t); + } + }); } @Override - public void postBatchData(byte[] signerId, int batchId, List batchDataList, FutureCallback callback) { + public void postBatchData(final BatchIdentifier batchIdentifier, final List batchChunkList, + final int startPosition, final FutureCallback callback) throws IllegalArgumentException{ + + if (!(batchIdentifier instanceof CachedClientBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + final CachedClientBatchIdentifier identifier = (CachedClientBatchIdentifier) batchIdentifier; + + localClient.postBatchData(identifier.getLocalIdentifier(), batchChunkList, startPosition, new FutureCallback() { + @Override + public void onSuccess(Boolean result) { + remoteClient.postBatchData(identifier.getRemoteIdentifier(), batchChunkList, startPosition, callback); + } + + @Override + public void onFailure(Throwable t) { + if (callback != null) + callback.onFailure(t); + } + }); } @Override - public void postBatchData(ByteString signerId, int batchId, List batchDataList, int startPosition, FutureCallback callback) { + public void postBatchData(final BatchIdentifier batchIdentifier, final List batchChunkList, final FutureCallback callback) + throws IllegalArgumentException{ + + if (!(batchIdentifier instanceof CachedClientBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + final CachedClientBatchIdentifier identifier = (CachedClientBatchIdentifier) batchIdentifier; + + localClient.postBatchData(identifier.getLocalIdentifier(), batchChunkList, new FutureCallback() { + @Override + public void onSuccess(Boolean result) { + remoteClient.postBatchData(identifier.getRemoteIdentifier(), batchChunkList, callback); + } + + @Override + public void onFailure(Throwable t) { + if (callback != null) + callback.onFailure(t); + } + }); } @Override - public void postBatchData(ByteString signerId, int batchId, List batchDataList, FutureCallback callback) { + public void closeBatch(final BatchIdentifier batchIdentifier, final Timestamp timestamp, final Iterable signatures, + final FutureCallback callback) { - } + if (!(batchIdentifier instanceof CachedClientBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } - @Override - public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback callback) { + final CachedClientBatchIdentifier identifier = (CachedClientBatchIdentifier) batchIdentifier; + + localClient.closeBatch(identifier.getLocalIdentifier(), timestamp, signatures, new FutureCallback() { + @Override + public void onSuccess(Boolean result) { + + remoteClient.closeBatch(identifier.getRemoteIdentifier(), timestamp, signatures, callback); + + } + + @Override + public void onFailure(Throwable t) { + if (callback != null) + callback.onFailure(t); + } + }); } @Override public void getRedundancy(MessageID id, FutureCallback callback) { - } - - @Override - public void readMessages(MessageFilterList filterList, FutureCallback> callback) { + remoteClient.getRedundancy(id, callback); } @Override - public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback callback) { + public void readMessages(MessageFilterList filterList, final FutureCallback> callback) { + + localClient.readMessages(filterList, callback); + + subscriber.subscribe(filterList, new SubscriptionStoreCallback(callback)); + + } + + @Override + public void readMessage(final MessageID msgID, final FutureCallback callback) { + + localClient.readMessage(msgID, new FutureCallback() { + + @Override + public void onSuccess(BulletinBoardMessage result) { + if (callback != null) + callback.onSuccess(result); // Read from local client was successful + } + + @Override + public void onFailure(Throwable t) { + + // Read from local unsuccessful: try to read from remote + + remoteClient.readMessage(msgID, new FutureCallback() { + + @Override + public void onSuccess(BulletinBoardMessage result) { + + // Read from remote was successful: store in local and return result + + localClient.postMessage(result, null); + + if (callback != null) + callback.onSuccess(result); + + } + + @Override + public void onFailure(Throwable t) { + + // Read from remote was unsuccessful: report error + if (callback != null) + callback.onFailure(t); + + } + + }); + + } + + }); + + } + + @Override + public void readBatchData(final BulletinBoardMessage stub, final FutureCallback callback) throws IllegalArgumentException { + + localClient.readBatchData(stub, new FutureCallback() { + + @Override + public void onSuccess(BulletinBoardMessage result) { + if (callback != null) + callback.onSuccess(result); // Read from local client was successful + } + + @Override + public void onFailure(Throwable t) { + + // Read from local unsuccessful: try to read from remote + + remoteClient.readBatchData(stub, new FutureCallback() { + + @Override + public void onSuccess(BulletinBoardMessage result) { + + // Read from remote was successful: store in local and return result + + localClient.postMessage(result, null); + + if (callback != null) + callback.onSuccess(result); + + } + + @Override + public void onFailure(Throwable t) { + + // Read from remote was unsuccessful: report error + if (callback != null) + callback.onFailure(t); + + } + + }); + + } + + }); } @Override public void querySync(SyncQuery syncQuery, FutureCallback callback) { + localClient.querySync(syncQuery, callback); + } @Override - public void init(BulletinBoardClientParams clientParams) { - - remoteClient.init(clientParams); - - ListeningScheduledExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(threadPoolSize)); - - List subscriberClients = new ArrayList<>(clientParams.getBulletinBoardAddressCount()); - - for (String address : clientParams.getBulletinBoardAddressList()){ - - SubscriptionAsyncBulletinBoardClient newClient = - new SingleServerBulletinBoardClient(executorService, failDelayInMilliseconds, subscriptionIntervalInMilliseconds); - - newClient.init(clientParams.toBuilder().clearBulletinBoardAddress().addBulletinBoardAddress(address).build()); - - subscriberClients.add(newClient); - - } - - subscriber = new ThreadedBulletinBoardSubscriber(subscriberClients, localClient); - - } + /** + * This is a stub method + * All resources are assumed to be initialized + */ + public void init(BulletinBoardClientParams clientParams) {} @Override public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { - return null; + return localClient.postMessage(msg); } @Override - public float getRedundancy(MessageID id) { - return 0; + public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException { + MessageID result = localClient.postAsBatch(msg, chunkSize); + remoteClient.postAsBatch(msg, chunkSize); + return result; } @Override - public List readMessages(MessageFilterList filterList) { - return null; + public float getRedundancy(MessageID id) throws CommunicationException { + return remoteClient.getRedundancy(id); } @Override - public SyncQuery generateSyncQuery(GenerateSyncQueryParams GenerateSyncQueryParams) throws CommunicationException { - return null; + public List readMessages(MessageFilterList filterList) throws CommunicationException { + subscriber.subscribe(filterList, new SubscriptionStoreCallback()); + return localClient.readMessages(filterList); + } + + @Override + public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException { + + BulletinBoardMessage result = null; + try { + result = localClient.readMessage(msgID); + } catch (CommunicationException e) { + //TODO: log + } + + if (result == null){ + result = remoteClient.readMessage(msgID); + + if (result != null){ + localClient.postMessage(result); + } + + } + + return result; + + } + + @Override + public BulletinBoardMessage readBatchData(BulletinBoardMessage stub) throws CommunicationException, IllegalArgumentException { + + BulletinBoardMessage result = null; + try { + result = localClient.readBatchData(stub); + } catch (CommunicationException e) { + //TODO: log + } + + if (result == null){ + result = remoteClient.readBatchData(stub); + + if (result != null){ + localClient.postMessage(result); + } + + } + + return result; + + } + + @Override + public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { + return localClient.generateSyncQuery(generateSyncQueryParams); } @Override public void close() { - + localClient.close(); + remoteClient.close(); + synchronizer.stop(); + try { + syncThread.join(); + } catch (InterruptedException e) { + //TODO: log interruption + } } @Override public void subscribe(MessageFilterList filterList, FutureCallback> callback) { - + subscriber.subscribe(filterList, new SubscriptionStoreCallback(callback)); } @Override public void subscribe(MessageFilterList filterList, long startEntry, FutureCallback> callback) { - + subscriber.subscribe(filterList, startEntry, new SubscriptionStoreCallback(callback)); } -} + +} \ No newline at end of file diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedClientBatchIdentifier.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedClientBatchIdentifier.java new file mode 100644 index 0000000..322473d --- /dev/null +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/CachedClientBatchIdentifier.java @@ -0,0 +1,29 @@ +package meerkat.bulletinboard; + +import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; + +import java.util.Arrays; + +/** + * Created by Arbel Deutsch Peled on 17-Jun-16. + */ +public final class CachedClientBatchIdentifier implements BatchIdentifier { + + // Per-server identifiers + private final BatchIdentifier localIdentifier; + private final BatchIdentifier remoteIdentifier; + + public CachedClientBatchIdentifier(BatchIdentifier localIdentifier, BatchIdentifier remoteIdentifier) { + this.localIdentifier = localIdentifier; + this.remoteIdentifier = remoteIdentifier; + } + + public BatchIdentifier getLocalIdentifier() { + return localIdentifier; + } + + public BatchIdentifier getRemoteIdentifier() { + return remoteIdentifier; + } + +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java index df3e196..2b904ed 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/LocalBulletinBoardClient.java @@ -1,20 +1,21 @@ package meerkat.bulletinboard; import com.google.common.util.concurrent.*; -import com.google.protobuf.ByteString; +import com.google.protobuf.Int64Value; +import com.google.protobuf.Timestamp; import meerkat.comm.CommunicationException; import meerkat.comm.MessageInputStream; import meerkat.comm.MessageInputStream.MessageInputStreamFactory; import meerkat.comm.MessageOutputStream; import meerkat.crypto.concrete.SHA256Digest; import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Crypto.Signature; import meerkat.protobuf.Voting.*; import meerkat.util.BulletinBoardUtils; import javax.ws.rs.NotFoundException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Executors; @@ -22,28 +23,27 @@ import java.util.concurrent.TimeUnit; /** * Created by Arbel Deutsch Peled on 15-Mar-16. - * This client is to be used mainly for testing. - * It wraps a BulletinBoardServer in an asynchronous client. + * This client wraps a BulletinBoardServer in an asynchronous client. + * It is meant to be used as a local cache handler and for testing purposes. * This means the access to the server is direct (via method calls) instead of through a TCP connection. * The client implements both synchronous and asynchronous method calls, but calls to the server itself are performed synchronously. */ -public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardClient{ +public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBoardClient { - private final BulletinBoardServer server; + private final DeletableBulletinBoardServer server; private final ListeningScheduledExecutorService executorService; - private final BatchDigest digest; - private final int subsrciptionDelay; + private final BulletinBoardDigest digest; + private final long subsrciptionDelay; /** * Initializes an instance of the client * @param server an initialized Bulletin Board Server instance which will perform the actual processing of the requests * @param threadNum is the number of concurrent threads to allocate for the client - * @param subscriptionDelay is the required delay between subscription calls in milliseconds */ - public LocalBulletinBoardClient(BulletinBoardServer server, int threadNum, int subscriptionDelay) { + public LocalBulletinBoardClient(DeletableBulletinBoardServer server, int threadNum, int subscriptionDelay) { this.server = server; this.executorService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(threadNum)); - this.digest = new GenericBatchDigest(new SHA256Digest()); + this.digest = new GenericBulletinBoardDigest(new SHA256Digest()); this.subsrciptionDelay = subscriptionDelay; } @@ -57,7 +57,7 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC @Override - public Boolean call() throws Exception { + public Boolean call() throws CommunicationException { return server.postMessage(msg).getValue(); } @@ -75,51 +75,57 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC private class CompleteBatchPoster implements Callable { - private final CompleteBatch completeBatch; + private final BulletinBoardMessage msg; + private final int chunkSize; - public CompleteBatchPoster(CompleteBatch completeBatch) { - this.completeBatch = completeBatch; + public CompleteBatchPoster(BulletinBoardMessage msg, int chunkSize) { + this.msg = msg; + this.chunkSize = chunkSize; } @Override - public Boolean call() throws Exception { + public Boolean call() throws CommunicationException { - if (!server.beginBatch(completeBatch.getBeginBatchMessage()).getValue()) - return false; + BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder() + .addAllTag(msg.getMsg().getTagList()) + .build(); + + Int64Value batchId = server.beginBatch(beginBatchMessage); + + BatchMessage.Builder builder = BatchMessage.newBuilder() + .setBatchId(batchId.getValue()); + + List batchChunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize); int i=0; - for (BatchData data : completeBatch.getBatchDataList()){ + for (BatchChunk chunk : batchChunkList){ - BatchMessage message = BatchMessage.newBuilder() - .setSignerId(completeBatch.getSignature().getSignerId()) - .setBatchId(completeBatch.getBeginBatchMessage().getBatchId()) - .setSerialNum(i) - .setData(data) - .build(); - - if (!server.postBatchMessage(message).getValue()) - return false; + server.postBatchMessage(builder.setSerialNum(i).setData(chunk).build()); i++; + } - return server.closeBatchMessage(completeBatch.getCloseBatchMessage()).getValue(); + CloseBatchMessage closeBatchMessage = BulletinBoardUtils.generateCloseBatchMessage(batchId, batchChunkList.size(), msg); + + return server.closeBatch(closeBatchMessage).getValue(); } } @Override - public MessageID postBatch(CompleteBatch completeBatch, FutureCallback callback) { + public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize, FutureCallback callback) { - Futures.addCallback(executorService.schedule(new CompleteBatchPoster(completeBatch), subsrciptionDelay, TimeUnit.MILLISECONDS), callback); + Futures.addCallback(executorService.submit(new CompleteBatchPoster(msg, chunkSize)), callback); - digest.update(completeBatch); + digest.reset(); + digest.update(msg); return digest.digestAsMessageID(); } - private class BatchBeginner implements Callable { + private class BatchBeginner implements Callable { private final BeginBatchMessage msg; @@ -129,28 +135,31 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC @Override - public Boolean call() throws Exception { - return server.beginBatch(msg).getValue(); + public SingleServerBatchIdentifier call() throws Exception { + return new SingleServerBatchIdentifier(server.beginBatch(msg)); } } @Override - public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback callback) { + public void beginBatch(Iterable tags, FutureCallback callback) { + + BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder() + .addAllTag(tags) + .build(); + Futures.addCallback(executorService.submit(new BatchBeginner(beginBatchMessage)), callback); } private class BatchDataPoster implements Callable { - private final ByteString signerId; - private final int batchId; - private final List batchDataList; + private final SingleServerBatchIdentifier batchId; + private final List batchChunkList; private final int startPosition; - public BatchDataPoster(ByteString signerId, int batchId, List batchDataList, int startPosition) { - this.signerId = signerId; + public BatchDataPoster(SingleServerBatchIdentifier batchId, List batchChunkList, int startPosition) { this.batchId = batchId; - this.batchDataList = batchDataList; + this.batchChunkList = batchChunkList; this.startPosition = startPosition; } @@ -159,11 +168,10 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC public Boolean call() throws Exception { BatchMessage.Builder msgBuilder = BatchMessage.newBuilder() - .setSignerId(signerId) - .setBatchId(batchId); + .setBatchId(batchId.getBatchId().getValue()); int i = startPosition; - for (BatchData data : batchDataList){ + for (BatchChunk data : batchChunkList){ msgBuilder.setSerialNum(i) .setData(data); @@ -175,6 +183,8 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC } + batchId.setLength(i); + return true; } @@ -182,24 +192,28 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC } @Override - public void postBatchData(byte[] signerId, int batchId, List batchDataList, int startPosition, FutureCallback callback) { - postBatchData(ByteString.copyFrom(signerId), batchId, batchDataList, startPosition, callback); + public void postBatchData(BatchIdentifier batchId, List batchChunkList, int startPosition, FutureCallback callback) + throws IllegalArgumentException{ + + // Cast identifier to usable form + + if (!(batchId instanceof SingleServerBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + SingleServerBatchIdentifier identifier = (SingleServerBatchIdentifier) batchId; + + // Add worker + + Futures.addCallback(executorService.submit(new BatchDataPoster(identifier, batchChunkList, startPosition)), callback); + } @Override - public void postBatchData(byte[] signerId, int batchId, List batchDataList, FutureCallback callback) { - postBatchData(signerId, batchId, batchDataList, 0, callback); + public void postBatchData(BatchIdentifier batchId, List batchChunkList, FutureCallback callback) throws IllegalArgumentException{ + postBatchData(batchId, batchChunkList, 0, callback); } - @Override - public void postBatchData(ByteString signerId, int batchId, List batchDataList, int startPosition, FutureCallback callback) { - Futures.addCallback(executorService.submit(new BatchDataPoster(signerId, batchId, batchDataList, startPosition)), callback); - } - - @Override - public void postBatchData(ByteString signerId, int batchId, List batchDataList, FutureCallback callback) { - postBatchData(signerId, batchId, batchDataList, 0, callback); - } private class BatchCloser implements Callable { @@ -212,14 +226,33 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC @Override public Boolean call() throws Exception { - return server.closeBatchMessage(msg).getValue(); + return server.closeBatch(msg).getValue(); } } @Override - public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback callback) { + public void closeBatch(BatchIdentifier batchId, Timestamp timestamp, Iterable signatures, FutureCallback callback) { + + // Cast identifier to usable form + + if (!(batchId instanceof SingleServerBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + SingleServerBatchIdentifier identifier = (SingleServerBatchIdentifier) batchId; + + // Add worker + + CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder() + .setBatchId(identifier.getBatchId().getValue()) + .setBatchLength(identifier.getLength()) + .setTimestamp(timestamp) + .addAllSig(signatures) + .build(); + Futures.addCallback(executorService.submit(new BatchCloser(closeBatchMessage)), callback); + } private class RedundancyGetter implements Callable { @@ -310,7 +343,8 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC public void onSuccess(List result) { // Report new messages to user - callback.onSuccess(result); + if (callback != null) + callback.onSuccess(result); MessageFilterList.Builder filterBuilder = filterList.toBuilder(); @@ -331,7 +365,7 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC filterList = filterBuilder.build(); // Reschedule job - Futures.addCallback(executorService.submit(new MessageReader(filterList)), this); + Futures.addCallback(executorService.schedule(new MessageReader(filterList), subsrciptionDelay, TimeUnit.MILLISECONDS), this); } @@ -339,7 +373,8 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC public void onFailure(Throwable t) { // Notify caller about failure and terminate subscription - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } } @@ -364,83 +399,122 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC subscribe(filterList, 0, callback); } - private class CompleteBatchReader implements Callable { + private class BatchDataReader implements Callable> { - private final BatchSpecificationMessage batchSpecificationMessage; + private final MessageID msgID; - public CompleteBatchReader(BatchSpecificationMessage batchSpecificationMessage) { - this.batchSpecificationMessage = batchSpecificationMessage; + public BatchDataReader(MessageID msgID) { + this.msgID = msgID; + } + + @Override + public List call() throws Exception { + + BatchQuery batchQuery = BatchQuery.newBuilder() + .setMsgID(msgID) + .setStartPosition(0) + .build(); + + ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); + MessageOutputStream batchOutputStream = new MessageOutputStream<>(byteOutputStream); + server.readBatch(batchQuery,batchOutputStream); + + MessageInputStream inputStream = + MessageInputStreamFactory.createMessageInputStream( + new ByteArrayInputStream(byteOutputStream.toByteArray()), + BatchChunk.class); + + return inputStream.asList(); + + } + } + + private class CompleteBatchReader implements Callable { + + private final MessageID msgID; + + public CompleteBatchReader(MessageID msgID) { + this.msgID = msgID; } @Override - public CompleteBatch call() throws Exception { + public BulletinBoardMessage call() throws Exception { - final String[] TAGS_TO_REMOVE = {BulletinBoardConstants.BATCH_TAG, BulletinBoardConstants.BATCH_ID_TAG_PREFIX}; - - CompleteBatch completeBatch = new CompleteBatch(BeginBatchMessage.newBuilder() - .setSignerId(batchSpecificationMessage.getSignerId()) - .setBatchId(batchSpecificationMessage.getBatchId()) - .build()); - - ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); - MessageOutputStream batchOutputStream = new MessageOutputStream<>(byteOutputStream); - server.readBatch(batchSpecificationMessage,batchOutputStream); - - MessageInputStream batchInputStream = - MessageInputStreamFactory.createMessageInputStream( - new ByteArrayInputStream(byteOutputStream.toByteArray()), - BatchData.class); - - completeBatch.appendBatchData(batchInputStream.asList()); + // Read message (mat be a stub) MessageFilterList filterList = MessageFilterList.newBuilder() .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(BulletinBoardConstants.BATCH_TAG) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(BulletinBoardConstants.BATCH_ID_TAG_PREFIX + completeBatch.getBeginBatchMessage().getBatchId()) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.SIGNER_ID) - .setId(completeBatch.getBeginBatchMessage().getSignerId()) + .setType(FilterType.MSG_ID) + .setId(msgID.getID()) .build()) .build(); - byteOutputStream = new ByteArrayOutputStream(); - MessageOutputStream messageOutputStream = new MessageOutputStream<>(byteOutputStream); - server.readMessages(filterList,messageOutputStream); + MessageReader messageReader = new MessageReader(filterList); + List bulletinBoardMessages = messageReader.call(); - MessageInputStream messageInputStream = - MessageInputStreamFactory.createMessageInputStream( - new ByteArrayInputStream(byteOutputStream.toByteArray()), - BulletinBoardMessage.class); + if (bulletinBoardMessages.size() <= 0) { + throw new NotFoundException("Message does not exist"); + } - if (!messageInputStream.isAvailable()) - throw new NotFoundException("Batch does not exist"); + BulletinBoardMessage msg = bulletinBoardMessages.get(0); - BulletinBoardMessage message = messageInputStream.readMessage(); + if (msg.getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { - completeBatch.setBeginBatchMessage(BeginBatchMessage.newBuilder() - .addAllTag(BulletinBoardUtils.removePrefixTags(message, Arrays.asList(TAGS_TO_REMOVE))) - .setSignerId(message.getSig(0).getSignerId()) - .setBatchId(Integer.parseInt(BulletinBoardUtils.findTagWithPrefix(message, BulletinBoardConstants.BATCH_ID_TAG_PREFIX))) - .build()); + // Read data - completeBatch.setSignature(message.getSig(0)); - completeBatch.setTimestamp(message.getMsg().getTimestamp()); + BatchDataReader batchDataReader = new BatchDataReader(msgID); + List batchChunkList = batchDataReader.call(); - return completeBatch; + // Combine and return + + return BulletinBoardUtils.gatherBatch(msg, batchChunkList); + + } else { + return msg; + } + + } + + } + + private class BatchDataCombiner implements Callable { + + private final BulletinBoardMessage stub; + + public BatchDataCombiner(BulletinBoardMessage stub) { + this.stub = stub; + } + + @Override + public BulletinBoardMessage call() throws Exception { + + MessageID msgID = MessageID.newBuilder().setID(stub.getMsg().getMsgId()).build(); + + BatchDataReader batchDataReader = new BatchDataReader(msgID); + + List batchChunkList = batchDataReader.call(); + + return BulletinBoardUtils.gatherBatch(stub, batchChunkList); } } @Override - public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback callback) { - Futures.addCallback(executorService.submit(new CompleteBatchReader(batchSpecificationMessage)), callback); + public void readMessage(MessageID msgID, FutureCallback callback) { + Futures.addCallback(executorService.submit(new CompleteBatchReader(msgID)), callback); + } + + @Override + public void readBatchData(BulletinBoardMessage stub, FutureCallback callback) throws IllegalArgumentException { + + if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID){ + throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); + } + + Futures.addCallback(executorService.submit(new BatchDataCombiner(stub)),callback); + } private class SyncQueryHandler implements Callable { @@ -474,17 +548,26 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC @Override public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { - try { - MessagePoster poster = new MessagePoster(msg); poster.call(); - digest.update(msg); + digest.update(msg.getMsg()); return digest.digestAsMessageID(); - } catch (Exception e) { - return null; - } + } + + @Override + public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException { + + CompleteBatchPoster poster = new CompleteBatchPoster(msg, chunkSize); + Boolean result = poster.call(); + + if (!result) + throw new CommunicationException("Batch post failed"); + + digest.reset(); + digest.update(msg); + return digest.digestAsMessageID(); } @@ -503,7 +586,7 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC } @Override - public List readMessages(MessageFilterList filterList) { + public List readMessages(MessageFilterList filterList) throws CommunicationException{ try { @@ -511,14 +594,89 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC return reader.call(); } catch (Exception e){ - return null; + throw new CommunicationException("Error reading from server"); } } @Override - public SyncQuery generateSyncQuery(GenerateSyncQueryParams GenerateSyncQueryParams) throws CommunicationException { - return server.generateSyncQuery(GenerateSyncQueryParams); + public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException { + + MessageFilterList filterList = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.MSG_ID) + .setId(msgID.getID()) + .build()) + .build(); + + CompleteBatchReader completeBatchReader = new CompleteBatchReader(msgID); + + try { + return completeBatchReader.call(); + } catch (Exception e) { + throw new CommunicationException(e.getMessage() + " " + e.getMessage()); + } + + } + + @Override + public BulletinBoardMessage readBatchData(BulletinBoardMessage stub) throws CommunicationException, IllegalArgumentException { + + if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID){ + throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); + } + + BatchDataCombiner combiner = new BatchDataCombiner(stub); + + try { + return combiner.call(); + } catch (Exception e) { + throw new CommunicationException(e.getCause() + " " + e.getMessage()); + } + + } + + @Override + public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { + return server.generateSyncQuery(generateSyncQueryParams); + } + + @Override + public void deleteMessage(MessageID msgID, FutureCallback callback) { + + try { + Boolean deleted = server.deleteMessage(msgID).getValue(); + if (callback != null) + callback.onSuccess(deleted); + } catch (CommunicationException e) { + if (callback != null) + callback.onFailure(e); + } + + } + + @Override + public void deleteMessage(long entryNum, FutureCallback callback) { + + try { + Boolean deleted = server.deleteMessage(entryNum).getValue(); + if (callback != null) + callback.onSuccess(deleted); + } catch (CommunicationException e) { + if (callback != null) + callback.onFailure(e); + } + + } + + @Override + public boolean deleteMessage(MessageID msgID) throws CommunicationException { + return server.deleteMessage(msgID).getValue(); + } + + @Override + public boolean deleteMessage(long entryNum) throws CommunicationException { + return server.deleteMessage(entryNum).getValue(); } @Override diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerBatchIdentifier.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerBatchIdentifier.java new file mode 100644 index 0000000..4934ee6 --- /dev/null +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerBatchIdentifier.java @@ -0,0 +1,27 @@ +package meerkat.bulletinboard; + +import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; + +import java.util.Arrays; + +/** + * Created by Arbel Deutsch Peled on 17-Jun-16. + */ +public final class MultiServerBatchIdentifier implements AsyncBulletinBoardClient.BatchIdentifier { + + // Per-server identifiers + private final Iterable identifiers; + + public MultiServerBatchIdentifier(Iterable identifiers) { + this.identifiers = identifiers; + } + + public MultiServerBatchIdentifier(BatchIdentifier[] identifiers) { + this.identifiers = Arrays.asList(identifiers); + } + + public Iterable getIdentifiers() { + return identifiers; + } + +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerWorker.java index 7347f47..0073be2 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/MultiServerWorker.java @@ -18,7 +18,7 @@ import java.util.concurrent.atomic.AtomicInteger; */ public abstract class MultiServerWorker extends BulletinClientWorker implements Runnable, FutureCallback{ - private final List clients; + protected final List clients; protected AtomicInteger minServers; // The minimal number of servers the job must be successful on for the job to be completed @@ -74,7 +74,8 @@ public abstract class MultiServerWorker extends BulletinClientWorker extends BulletinClientWorker getClientIterator() { - return clients.iterator(); - } - protected int getClientNumber() { return clients.size(); } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java index 74eacae..a274f53 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java @@ -1,192 +1,329 @@ -package meerkat.bulletinboard; - -import com.google.protobuf.BoolValue; -import com.google.protobuf.ByteString; -import meerkat.comm.CommunicationException; -import meerkat.crypto.Digest; -import meerkat.crypto.concrete.SHA256Digest; -import meerkat.protobuf.BulletinBoardAPI.*; -import meerkat.protobuf.Comm.*; -import meerkat.protobuf.Voting.*; -import meerkat.rest.*; - -import java.util.List; - -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.Response; - -import static meerkat.bulletinboard.BulletinBoardConstants.*; - -/** - * Created by Arbel Deutsch Peled on 05-Dec-15. - * Implements BulletinBoardClient interface in a simple, straightforward manner - */ -public class SimpleBulletinBoardClient implements BulletinBoardClient{ - - protected List meerkatDBs; - - protected Client client; - - protected Digest digest; - - /** - * Stores database locations and initializes the web Client - * @param clientParams contains the data needed to access the DBs - */ - @Override - public void init(BulletinBoardClientParams clientParams) { - - this.meerkatDBs = clientParams.getBulletinBoardAddressList(); - - client = ClientBuilder.newClient(); - client.register(ProtobufMessageBodyReader.class); - client.register(ProtobufMessageBodyWriter.class); - - digest = new SHA256Digest(); - - } - - /** - * Post message to all DBs - * Make only one try per DB. - * @param msg is the message, - * @return the message ID for later retrieval - * @throws CommunicationException - */ - @Override - public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { - - WebTarget webTarget; - Response response; - - // Post message to all databases - try { - for (String db : meerkatDBs) { - webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(POST_MESSAGE_PATH); - response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF)); - - // Only consider valid responses - if (response.getStatusInfo() == Response.Status.OK - || response.getStatusInfo() == Response.Status.CREATED) { - response.readEntity(BoolValue.class).getValue(); - } - } - } catch (Exception e) { // Occurs only when server replies with valid status but invalid data - throw new CommunicationException("Error accessing database: " + e.getMessage()); - } - - // Calculate the correct message ID and return it - digest.reset(); - digest.update(msg.getMsg()); - return MessageID.newBuilder().setID(ByteString.copyFrom(digest.digest())).build(); - } - - /** - * Access each database and search for a given message ID - * Return the number of databases in which the message was found - * Only try once per DB - * Ignore communication exceptions in specific databases - * @param id is the requested message ID - * @return the number of DBs in which retrieval was successful - */ - @Override - public float getRedundancy(MessageID id) { - WebTarget webTarget; - Response response; - - MessageFilterList filterList = MessageFilterList.newBuilder() - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.MSG_ID) - .setId(id.getID()) - .build()) - .build(); - - float count = 0; - - for (String db : meerkatDBs) { - try { - webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH); - - response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF)); - - if (response.readEntity(BulletinBoardMessageList.class).getMessageCount() > 0){ - count++; - } - - } catch (Exception e) {} - } - - return count / ((float) meerkatDBs.size()); - } - - /** - * Go through the DBs and try to retrieve messages according to the specified filter - * If at the operation is successful for some DB: return the results and stop iterating - * If no operation is successful: return null (NOT blank list) - * @param filterList return only messages that match the filters (null means no filtering). - * @return the list of Bulletin Board messages that are returned from a server - */ - @Override - public List readMessages(MessageFilterList filterList) { - - WebTarget webTarget; - Response response; - BulletinBoardMessageList messageList; - - // Replace null filter list with blank one. - if (filterList == null){ - filterList = MessageFilterList.newBuilder().build(); - } - - for (String db : meerkatDBs) { - - try { - webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH); - - response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF)); - - messageList = response.readEntity(BulletinBoardMessageList.class); - - if (messageList != null){ - return messageList.getMessageList(); - } - - } catch (Exception e) {} - - } - - return null; - - } - - @Override - public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { - - WebTarget webTarget; - Response response; - - for (String db : meerkatDBs) { - - try { - webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(GENERATE_SYNC_QUERY_PATH); - - response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(generateSyncQueryParams, Constants.MEDIATYPE_PROTOBUF)); - - return response.readEntity(SyncQuery.class); - - } catch (Exception e) {} - - } - - throw new CommunicationException("Could not contact any server"); - - } - - public void close() { - client.close(); - } - -} +package meerkat.bulletinboard; + +import com.google.protobuf.BoolValue; +import com.google.protobuf.ByteString; +import com.google.protobuf.Int64Value; +import meerkat.bulletinboard.workers.singleserver.*; +import meerkat.comm.CommunicationException; +import meerkat.crypto.concrete.SHA256Digest; +import meerkat.protobuf.BulletinBoardAPI; +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Voting.*; +import meerkat.rest.*; +import meerkat.util.BulletinBoardUtils; + +import java.util.List; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; + +import static meerkat.bulletinboard.BulletinBoardConstants.*; + +/** + * Created by Arbel Deutsch Peled on 05-Dec-15. + * Implements BulletinBoardClient interface in a simple, straightforward manner + */ +public class SimpleBulletinBoardClient implements BulletinBoardClient{ + + protected List meerkatDBs; + + protected Client client; + + protected BulletinBoardDigest digest; + + /** + * Stores database locations and initializes the web Client + * @param clientParams contains the data needed to access the DBs + */ + @Override + public void init(BulletinBoardClientParams clientParams) { + + this.meerkatDBs = clientParams.getBulletinBoardAddressList(); + + client = ClientBuilder.newClient(); + client.register(ProtobufMessageBodyReader.class); + client.register(ProtobufMessageBodyWriter.class); + + // Wrap the Digest into a BatchDigest + digest = new GenericBulletinBoardDigest(new SHA256Digest()); + + } + + /** + * Post message to all DBs + * Make only one try per DB. + * @param msg is the message, + * @return the message ID for later retrieval + * @throws CommunicationException + */ + @Override + public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { + + WebTarget webTarget; + Response response = null; + + // Post message to all databases + try { + for (String db : meerkatDBs) { + + SingleServerPostMessageWorker worker = new SingleServerPostMessageWorker(db, msg, 0); + + worker.call(); + + } + } catch (Exception e) { // Occurs only when server replies with valid status but invalid data + throw new CommunicationException("Error accessing database: " + e.getMessage()); + } + + // Calculate the correct message ID and return it + digest.reset(); + digest.update(msg.getMsg()); + return MessageID.newBuilder().setID(ByteString.copyFrom(digest.digest())).build(); + } + + /** + * Access each database and search for a given message ID + * Return the number of databases in which the message was found + * Only try once per DB + * Ignore communication exceptions in specific databases + * @param id is the requested message ID + * @return the number of DBs in which retrieval was successful + */ + @Override + public float getRedundancy(MessageID id) { + WebTarget webTarget; + Response response; + + MessageFilterList filterList = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.MSG_ID) + .setId(id.getID()) + .build()) + .build(); + + float count = 0; + + for (String db : meerkatDBs) { + try { + webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH); + + response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF)); + + if (response.readEntity(BulletinBoardMessageList.class).getMessageCount() > 0){ + count++; + } + + } catch (Exception e) {} + } + + return count / ((float) meerkatDBs.size()); + } + + /** + * Go through the DBs and try to retrieve messages according to the specified filter + * If at the operation is successful for some DB: return the results and stop iterating + * If no operation is successful: return null (NOT blank list) + * @param filterList return only messages that match the filters (null means no filtering). + * @return the list of Bulletin Board messages that are returned from a server + */ + @Override + public List readMessages(MessageFilterList filterList) throws CommunicationException{ + + // Replace null filter list with blank one. + if (filterList == null){ + filterList = MessageFilterList.getDefaultInstance(); + } + + String exceptionString = ""; + + for (String db : meerkatDBs) { + + try { + + SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(db, filterList, 0); + + List result = worker.call(); + + return result; + + } catch (Exception e) { + //TODO: log + exceptionString += e.getMessage() + "\n"; + } + } + + throw new CommunicationException("Could not find message in any DB. Errors follow:\n" + exceptionString); + + } + + @Override + public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException { + + List chunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize); + + BeginBatchMessage beginBatchMessage = BulletinBoardUtils.generateBeginBatchMessage(msg); + + boolean posted = false; + + // Post message to all databases + + for (String db : meerkatDBs) { + + try { + + int pos = 0; + + SingleServerBeginBatchWorker beginBatchWorker = new SingleServerBeginBatchWorker(db, beginBatchMessage, 0); + + Int64Value batchId = beginBatchWorker.call(); + + BatchMessage.Builder builder = BatchMessage.newBuilder().setBatchId(batchId.getValue()); + + for (BatchChunk batchChunk : chunkList) { + + SingleServerPostBatchWorker postBatchWorker = + new SingleServerPostBatchWorker( + db, + builder.setData(batchChunk).setSerialNum(pos).build(), + 0); + + postBatchWorker.call(); + + pos++; + + } + + CloseBatchMessage closeBatchMessage = BulletinBoardUtils.generateCloseBatchMessage(batchId, chunkList.size(), msg); + + SingleServerCloseBatchWorker closeBatchWorker = new SingleServerCloseBatchWorker(db, closeBatchMessage, 0); + + closeBatchWorker.call(); + + posted = true; + + } catch(Exception ignored) {} + + } + + if (!posted){ + throw new CommunicationException("Could not post to any server"); + } + + digest.reset(); + digest.update(msg); + return digest.digestAsMessageID(); + + } + + @Override + public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException { + + MessageFilterList filterList = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.MSG_ID) + .setId(msgID.getID()) + .build()) + .build(); + + BatchQuery batchQuery = BatchQuery.newBuilder() + .setMsgID(msgID) + .setStartPosition(0) + .build(); + + String exceptionString = ""; + + for (String db : meerkatDBs) { + + try { + SingleServerReadMessagesWorker messagesWorker = new SingleServerReadMessagesWorker(db, filterList, 0); + + List messages = messagesWorker.call(); + + if (messages == null || messages.size() < 1) + continue; + + BulletinBoardMessage stub = messages.get(0); + + SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(db, batchQuery, 0); + + List batchChunkList = batchWorker.call(); + + return BulletinBoardUtils.gatherBatch(stub, batchChunkList); + + } catch (Exception e) { + //TODO: log + exceptionString += e.getMessage() + "\n"; + } + } + + throw new CommunicationException("Could not find message in any DB. Errors follow:\n" + exceptionString); + + } + + @Override + public BulletinBoardMessage readBatchData(BulletinBoardMessage stub) throws CommunicationException, IllegalArgumentException { + + if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID){ + throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); + } + + BatchQuery batchQuery = BatchQuery.newBuilder() + .setMsgID(MessageID.newBuilder() + .setID(stub.getMsg().getMsgId()) + .build()) + .setStartPosition(0) + .build(); + + String exceptionString = ""; + + for (String db : meerkatDBs) { + + try { + + SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(db, batchQuery, 0); + + List batchChunkList = batchWorker.call(); + + return BulletinBoardUtils.gatherBatch(stub, batchChunkList); + + } catch (Exception e) { + //TODO: log + exceptionString += e.getMessage() + "\n"; + } + } + + throw new CommunicationException("Could not find message in any DB. Errors follow:\n" + exceptionString); + + } + + @Override + public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { + + WebTarget webTarget; + Response response; + + for (String db : meerkatDBs) { + + try { + webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(GENERATE_SYNC_QUERY_PATH); + + response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(generateSyncQueryParams, Constants.MEDIATYPE_PROTOBUF)); + + return response.readEntity(SyncQuery.class); + + } catch (Exception e) {} + + } + + throw new CommunicationException("Could not contact any server"); + + } + + public void close() { + client.close(); + } + +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java new file mode 100644 index 0000000..5cdf73b --- /dev/null +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardSynchronizer.java @@ -0,0 +1,241 @@ +package meerkat.bulletinboard; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.protobuf.ByteString; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.util.BulletinBoardUtils; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Created by Arbel on 13/04/2016. + * Simple, straightforward implementation of the {@link BulletinBoardSynchronizer} interface + */ +public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronizer { + + private DeletableSubscriptionBulletinBoardClient localClient; + private AsyncBulletinBoardClient remoteClient; + + private AtomicBoolean running; + private volatile SyncStatus syncStatus; + + private List> messageCountCallbacks; + private List> syncStatusCallbacks; + + private static final MessageFilterList EMPTY_FILTER = MessageFilterList.getDefaultInstance(); + private static final int DEFAULT_SLEEP_INTERVAL = 10000; // 10 Seconds + private static final int DEFAULT_WAIT_CAP = 300000; // 5 minutes wait before deciding that the sync has failed fatally + + private final int SLEEP_INTERVAL; + private final int WAIT_CAP; + + private Semaphore semaphore; + + private class SyncCallback implements FutureCallback> { + + @Override + public void onSuccess(List result) { + + // Notify Message Count callbacks if needed + + if (syncStatus != SyncStatus.SYNCHRONIZED || result.size() > 0) { + + for (FutureCallback callback : messageCountCallbacks){ + callback.onSuccess(result.size()); + } + + } + + // Handle upload and status change + + SyncStatus newStatus = SyncStatus.PENDING; + + if (result.size() == 0) { + newStatus = SyncStatus.SYNCHRONIZED; + semaphore.release(); + } + + else{ // Upload messages + + for (BulletinBoardMessage message : result){ + + try { + + if (message.getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { + + // This is a batch message: need to upload batch data as well as the message itself + + BulletinBoardMessage completeMsg = localClient.readBatchData(message); + + remoteClient.postMessage(completeMsg); + + localClient.deleteMessage(completeMsg.getEntryNum()); + + + } else { + + // This is a regular message: post it + remoteClient.postMessage(message); + + localClient.deleteMessage(message.getEntryNum()); + + } + + } catch (CommunicationException e) { + // This is an error with the local server + // TODO: log + updateSyncStatus(SyncStatus.SERVER_ERROR); + } + + } + + } + + updateSyncStatus(newStatus); + + } + + @Override + public void onFailure(Throwable t) { + + updateSyncStatus(SyncStatus.SERVER_ERROR); + + } + + } + + public SimpleBulletinBoardSynchronizer(int sleepInterval, int waitCap) { + this.syncStatus = SyncStatus.STOPPED; + this.SLEEP_INTERVAL = sleepInterval; + this.WAIT_CAP = waitCap; + this.running = new AtomicBoolean(false); + } + + public SimpleBulletinBoardSynchronizer() { + this(DEFAULT_SLEEP_INTERVAL, DEFAULT_WAIT_CAP); + } + + private synchronized void updateSyncStatus(SyncStatus newStatus) { + + if (!running.get()) { + + newStatus = SyncStatus.STOPPED; + + } + + if (newStatus != syncStatus){ + + syncStatus = newStatus; + + for (FutureCallback callback : syncStatusCallbacks){ + if (callback != null) + callback.onSuccess(syncStatus); + } + + } + + } + + @Override + public void init(DeletableSubscriptionBulletinBoardClient localClient, AsyncBulletinBoardClient remoteClient) { + + updateSyncStatus(SyncStatus.STOPPED); + + this.localClient = localClient; + this.remoteClient = remoteClient; + + messageCountCallbacks = new LinkedList<>(); + syncStatusCallbacks = new LinkedList<>(); + + semaphore = new Semaphore(0); + + } + + @Override + public SyncStatus getSyncStatus() { + return syncStatus; + } + + @Override + public void subscribeToSyncStatus(FutureCallback callback) { + syncStatusCallbacks.add(callback); + } + + @Override + public List getRemainingMessages() throws CommunicationException{ + return localClient.readMessages(EMPTY_FILTER); + } + + @Override + public void getRemainingMessages(FutureCallback> callback) { + localClient.readMessages(EMPTY_FILTER, callback); + } + + @Override + public long getRemainingMessagesCount() throws CommunicationException { + return localClient.readMessages(EMPTY_FILTER).size(); + } + + @Override + public void subscribeToRemainingMessagesCount(FutureCallback callback) { + messageCountCallbacks.add(callback); + } + + @Override + public void run() { + + if (running.compareAndSet(false,true)){ + + updateSyncStatus(SyncStatus.PENDING); + SyncCallback callback = new SyncCallback(); + + while (syncStatus != SyncStatus.STOPPED) { + + do { + + localClient.readMessages(EMPTY_FILTER, callback); + + 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) { + //TODO: log (probably nudged) + } + + } + + } + + } + + @Override + public void nudge() { + semaphore.release(); + } + + @Override + public void stop() { + + running.set(false); + updateSyncStatus(SyncStatus.STOPPED); + + } + +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBatchIdentifier.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBatchIdentifier.java new file mode 100644 index 0000000..9c4ad40 --- /dev/null +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBatchIdentifier.java @@ -0,0 +1,42 @@ +package meerkat.bulletinboard; + +import com.google.protobuf.Int64Value; + +/** + * Created by Arbel Deutsch Peled on 16-Jun-16. + * Single-server implementation of the BatchIdentifier interface + */ +final class SingleServerBatchIdentifier implements AsyncBulletinBoardClient.BatchIdentifier { + + private final Int64Value batchId; + + private int length; + + public SingleServerBatchIdentifier(Int64Value batchId) { + this.batchId = batchId; + length = 0; + } + + public SingleServerBatchIdentifier(long batchId) { + this(Int64Value.newBuilder().setValue(batchId).build()); + } + + public Int64Value getBatchId() { + return batchId; + } + + /** + * Overrides the existing length with the new one only if the new length is longer + * @param newLength + */ + public void setLength(int newLength) { + if (newLength > length) { + length = newLength; + } + } + + public int getLength() { + return length; + } + +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java index 5edd079..e732059 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/SingleServerBulletinBoardClient.java @@ -4,16 +4,19 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListeningScheduledExecutorService; import com.google.common.util.concurrent.MoreExecutors; -import com.google.protobuf.ByteString; +import com.google.protobuf.Int64Value; +import com.google.protobuf.Timestamp; import meerkat.bulletinboard.workers.singleserver.*; import meerkat.comm.CommunicationException; +import meerkat.crypto.concrete.SHA256Digest; import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Crypto; import meerkat.protobuf.Voting.BulletinBoardClientParams; import meerkat.util.BulletinBoardUtils; -import java.util.Arrays; +import javax.ws.rs.client.Client; +import java.lang.Iterable; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -28,19 +31,23 @@ import java.util.concurrent.atomic.AtomicInteger; * If the list of servers contains more than one server: the server actually used is the first one * The class further implements a delayed access to the server after a communication error occurs */ -public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient implements SubscriptionAsyncBulletinBoardClient { +public class SingleServerBulletinBoardClient implements SubscriptionBulletinBoardClient { + + protected Client client; + + protected BulletinBoardDigest digest; + + private String dbAddress; private final int MAX_RETRIES = 11; - private ListeningScheduledExecutorService executorService; - - protected BatchDigest batchDigest; + private final ListeningScheduledExecutorService executorService; private long lastServerErrorTime; - private final long failDelayInMilliseconds; + private final long FAIL_DELAY_IN_MILLISECONDS; - private final long subscriptionIntervalInMilliseconds; + private final long SUBSCRIPTION_INTERVAL_IN_MILLISECONDS; /** * Notify the client that a job has failed @@ -53,6 +60,43 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i } + private class SynchronousRetry { + + private final SingleServerWorker worker; + + private String thrown; + + public SynchronousRetry(SingleServerWorker worker) { + this.worker = worker; + this.thrown = "Could not contact server. Errors follow:\n"; + } + + OUT run() throws CommunicationException { + + do { + + try { + return worker.call(); + } catch (Exception e) { + thrown += e.getCause() + " " + e.getMessage() + "\n"; + } + + try { + Thread.sleep(FAIL_DELAY_IN_MILLISECONDS); + } catch (InterruptedException e) { + //TODO: log + } + + worker.decMaxRetry(); + + } while (worker.isRetry()); + + throw new CommunicationException(thrown); + + } + + } + /** * This method adds a worker to the scheduled queue of the threadpool * If the server is in an accessible state: the job is submitted for immediate handling @@ -64,7 +108,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i long timeSinceLastServerError = System.currentTimeMillis() - lastServerErrorTime; - if (timeSinceLastServerError >= failDelayInMilliseconds) { + if (timeSinceLastServerError >= FAIL_DELAY_IN_MILLISECONDS) { // Schedule for immediate processing Futures.addCallback(executorService.submit(worker), callback); @@ -74,7 +118,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i // Schedule for processing immediately following delay expiry Futures.addCallback(executorService.schedule( worker, - failDelayInMilliseconds - timeSinceLastServerError, + FAIL_DELAY_IN_MILLISECONDS - timeSinceLastServerError, TimeUnit.MILLISECONDS), callback); @@ -97,7 +141,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i @Override public void onSuccess(T result) { - futureCallback.onSuccess(result); + if (futureCallback != null) + futureCallback.onSuccess(result); } @Override @@ -115,7 +160,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i scheduleWorker(worker, this); } else { // No more retries: notify caller about failure - futureCallback.onFailure(t); + if (futureCallback != null) + futureCallback.onFailure(t); } } @@ -127,14 +173,14 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i * It reports success back to the user only if all of the batch-data were successfully posted * If any batch-data fails to post: this callback reports failure */ - class PostBatchDataListCallback implements FutureCallback { + class PostBatchChunkListCallback implements FutureCallback { private final FutureCallback callback; private AtomicInteger batchDataRemaining; private AtomicBoolean aggregatedResult; - public PostBatchDataListCallback(int batchDataLength, FutureCallback callback) { + public PostBatchChunkListCallback(int batchDataLength, FutureCallback callback) { this.callback = callback; this.batchDataRemaining = new AtomicInteger(batchDataLength); @@ -150,7 +196,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i } if (batchDataRemaining.decrementAndGet() == 0){ - callback.onSuccess(this.aggregatedResult.get()); + if (callback != null) + callback.onSuccess(this.aggregatedResult.get()); } } @@ -158,110 +205,80 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i public void onFailure(Throwable t) { // Notify caller about failure - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } } + private class ReadBatchCallback implements FutureCallback> { + + private final BulletinBoardMessage stub; + private final FutureCallback callback; + + public ReadBatchCallback(BulletinBoardMessage stub, FutureCallback callback) { + this.stub = stub; + this.callback = callback; + } + + @Override + public void onSuccess(List result) { + callback.onSuccess(BulletinBoardUtils.gatherBatch(stub, result)); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + + } + /** - * This callback ties together the different parts of a CompleteBatch as they arrive from the server - * It assembles a CompleteBatch from the parts and sends it to the user if all parts arrived - * If any part fails to arrive: it invokes the onFailure method + * This callback receives a message which may be a stub + * If the message is not a stub: it returns it as is to a callback function + * If it is a stub: it schedules a read of the batch data which will return a complete message to the callback function */ - class CompleteBatchReadCallback { + class CompleteMessageReadCallback implements FutureCallback>{ - private final FutureCallback callback; + private final FutureCallback callback; - private List batchDataList; - private BulletinBoardMessage batchMessage; - - private AtomicInteger remainingQueries; - private AtomicBoolean failed; - - public CompleteBatchReadCallback(FutureCallback callback) { + public CompleteMessageReadCallback(FutureCallback callback) { this.callback = callback; - remainingQueries = new AtomicInteger(2); - failed = new AtomicBoolean(false); - } - protected void combineAndReturn() { + @Override + public void onSuccess(List result) { + if (result.size() <= 0) { + onFailure(new CommunicationException("Could not find required message on the server.")); + } else { - final String[] prefixes = { - BulletinBoardConstants.BATCH_ID_TAG_PREFIX, - BulletinBoardConstants.BATCH_TAG}; + BulletinBoardMessage msg = result.get(0); - if (remainingQueries.decrementAndGet() == 0){ + if (msg.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { + callback.onSuccess(msg); + } else { - String batchIdStr = BulletinBoardUtils.findTagWithPrefix(batchMessage, BulletinBoardConstants.BATCH_ID_TAG_PREFIX); + // 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(dbAddress, batchQuery, MAX_RETRIES); + + scheduleWorker(batchWorker, new ReadBatchCallback(msg, callback)); - if (batchIdStr == null){ - callback.onFailure(new CommunicationException("Server returned invalid message with no Batch ID tag")); } - - BeginBatchMessage beginBatchMessage = - BeginBatchMessage.newBuilder() - .setSignerId(batchMessage.getSig(0).getSignerId()) - .setBatchId(Integer.parseInt(batchIdStr)) - .addAllTag(BulletinBoardUtils.removePrefixTags(batchMessage, Arrays.asList(prefixes))) - .build(); - callback.onSuccess(new CompleteBatch(beginBatchMessage, batchDataList, batchMessage.getSig(0))); - } - - } - - protected void fail(Throwable t) { - if (failed.compareAndSet(false, true)) { - callback.onFailure(t); } } - /** - * @return a FutureCallback for the Batch Data List that ties to this object - */ - public FutureCallback> asBatchDataListFutureCallback() { - return new FutureCallback>() { - - @Override - public void onSuccess(List result) { - batchDataList = result; - - combineAndReturn(); - } - - @Override - public void onFailure(Throwable t) { - fail(t); - } - - }; - } - - /** - * @return a FutureCallback for the Bulletin Board Message that ties to this object - */ - public FutureCallback> asBulletinBoardMessageListFutureCallback() { - return new FutureCallback>() { - - @Override - public void onSuccess(List result) { - if (result.size() < 1){ - onFailure(new IllegalArgumentException("Server returned empty message list")); - return; - } - - batchMessage = result.get(0); - - combineAndReturn(); - } - - @Override - public void onFailure(Throwable t) { - fail(t); - } - }; + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); } } @@ -289,22 +306,31 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i public void onSuccess(List result) { // Report new messages to user - callback.onSuccess(result); + if (callback != null) + callback.onSuccess(result); - // Remove last filter from list (MIN_ENTRY one) - filterBuilder.removeFilter(filterBuilder.getFilterCount() - 1); + // Update filter if needed - // Add updated MIN_ENTRY filter (entry number is successor of last received entry's number) - filterBuilder.addFilter(MessageFilter.newBuilder() - .setType(FilterType.MIN_ENTRY) - .setEntry(result.get(result.size() - 1).getEntryNum() + 1) - .build()); + if (result.size() > 0) { + + // Remove last filter from list (MIN_ENTRY one) + filterBuilder.removeFilter(filterBuilder.getFilterCount() - 1); + + // Add updated MIN_ENTRY filter (entry number is successor of last received entry's number) + filterBuilder.addFilter(MessageFilter.newBuilder() + .setType(FilterType.MIN_ENTRY) + .setEntry(result.get(result.size() - 1).getEntryNum() + 1) + .build()); + + } // Create new worker with updated task - worker = new SingleServerReadMessagesWorker(worker.serverAddress, filterBuilder.build(), 1); + worker = new SingleServerReadMessagesWorker(worker.serverAddress, filterBuilder.build(), MAX_RETRIES); - // Schedule the worker - scheduleWorker(worker, this); + RetryCallback> retryCallback = new RetryCallback<>(worker, this); + + // Schedule the worker to run after the given interval has elapsed + Futures.addCallback(executorService.schedule(worker, SUBSCRIPTION_INTERVAL_IN_MILLISECONDS, TimeUnit.MILLISECONDS), retryCallback); } @@ -315,7 +341,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i fail(); // Notify caller about failure and terminate subscription - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } } @@ -325,8 +352,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i this.executorService = executorService; - this.failDelayInMilliseconds = failDelayInMilliseconds; - this.subscriptionIntervalInMilliseconds = subscriptionIntervalInMilliseconds; + this.FAIL_DELAY_IN_MILLISECONDS = failDelayInMilliseconds; + this.SUBSCRIPTION_INTERVAL_IN_MILLISECONDS = subscriptionIntervalInMilliseconds; // Set server error time to a time sufficiently in the past to make new jobs go through lastServerErrorTime = System.currentTimeMillis() - failDelayInMilliseconds; @@ -348,79 +375,289 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i @Override public void init(BulletinBoardClientParams clientParams) { - // Perform usual setup - super.init(clientParams); - - // Wrap the Digest into a BatchDigest - batchDigest = new GenericBatchDigest(digest); + this.digest = new GenericBulletinBoardDigest(new SHA256Digest()); // Remove all but first DB address - String dbAddress = meerkatDBs.get(0); - meerkatDBs = new LinkedList<>(); - meerkatDBs.add(dbAddress); + this.dbAddress = clientParams.getBulletinBoardAddress(0); } + // Synchronous methods + + @Override + public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { + + SingleServerPostMessageWorker worker = new SingleServerPostMessageWorker(dbAddress, msg, MAX_RETRIES); + + SynchronousRetry retry = new SynchronousRetry<>(worker); + + retry.run(); + + digest.reset(); + digest.update(msg); + + return digest.digestAsMessageID(); + + } + + @Override + public float getRedundancy(MessageID id) throws CommunicationException { + + SingleServerGetRedundancyWorker worker = new SingleServerGetRedundancyWorker(dbAddress, id, MAX_RETRIES); + + SynchronousRetry retry = new SynchronousRetry<>(worker); + + return retry.run(); + + } + + @Override + public List readMessages(MessageFilterList filterList) throws CommunicationException { + + SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(dbAddress, filterList, MAX_RETRIES); + + SynchronousRetry> retry = new SynchronousRetry<>(worker); + + return retry.run(); + + } + + @Override + public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException { + + // Begin the batch and obtain identifier + + BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder() + .addAllTag(msg.getMsg().getTagList()) + .build(); + + SingleServerBeginBatchWorker beginBatchWorker = new SingleServerBeginBatchWorker(dbAddress, beginBatchMessage, MAX_RETRIES); + + SynchronousRetry beginRetry = new SynchronousRetry<>(beginBatchWorker); + + Int64Value identifier = beginRetry.run(); + + // Post data chunks + + List batchChunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize); + + BatchMessage.Builder builder = BatchMessage.newBuilder().setBatchId(identifier.getValue()); + + int position = 0; + + for (BatchChunk data : batchChunkList) { + + builder.setSerialNum(position).setData(data); + + SingleServerPostBatchWorker dataWorker = new SingleServerPostBatchWorker(dbAddress, builder.build(), MAX_RETRIES); + + SynchronousRetry dataRetry = new SynchronousRetry<>(dataWorker); + + dataRetry.run(); + + // Increment position in batch + position++; + + } + + // Close batch + + CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder() + .setBatchId(identifier.getValue()) + .addAllSig(msg.getSigList()) + .setTimestamp(msg.getMsg().getTimestamp()) + .setBatchLength(position) + .build(); + + SingleServerCloseBatchWorker closeBatchWorker = new SingleServerCloseBatchWorker(dbAddress, closeBatchMessage, MAX_RETRIES); + + SynchronousRetry retry = new SynchronousRetry<>(closeBatchWorker); + + retry.run(); + + // Calculate ID and return + + digest.reset(); + digest.update(msg); + + return digest.digestAsMessageID(); + + } + + @Override + public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException { + + // Retrieve message (which may be a stub) + + MessageFilterList filterList = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.MSG_ID) + .setId(msgID.getID()) + .build()) + .build(); + + SingleServerReadMessagesWorker stubWorker = new SingleServerReadMessagesWorker(dbAddress, filterList, MAX_RETRIES); + + SynchronousRetry> retry = new SynchronousRetry<>(stubWorker); + + List messages = retry.run(); + + if (messages.size() <= 0) { + throw new CommunicationException("Could not find message in database."); + } + + BulletinBoardMessage msg = messages.get(0); + + if (msg.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { + + // We retrieved a complete message. Return it. + return msg; + + } else { + + // We retrieved a stub. Retrieve data. + return readBatchData(msg); + + } + + } + + @Override + public BulletinBoardMessage readBatchData(BulletinBoardMessage stub) throws CommunicationException, IllegalArgumentException { + + BatchQuery batchQuery = BatchQuery.newBuilder() + .setMsgID(MessageID.newBuilder() + .setID(stub.getMsg().getMsgId()) + .build()) + .setStartPosition(0) + .build(); + + SingleServerReadBatchWorker readBatchWorker = new SingleServerReadBatchWorker(dbAddress, batchQuery, MAX_RETRIES); + + SynchronousRetry> batchRetry = new SynchronousRetry<>(readBatchWorker); + + List batchChunkList = batchRetry.run(); + + return BulletinBoardUtils.gatherBatch(stub, batchChunkList); + + } + + @Override + public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { + + SingleServerGenerateSyncQueryWorker worker = + new SingleServerGenerateSyncQueryWorker(dbAddress, generateSyncQueryParams, MAX_RETRIES); + + SynchronousRetry retry = new SynchronousRetry<>(worker); + + return retry.run(); + + } + + // Asynchronous methods + @Override public MessageID postMessage(BulletinBoardMessage msg, FutureCallback callback) { // Create worker with redundancy 1 and MAX_RETRIES retries - SingleServerPostMessageWorker worker = new SingleServerPostMessageWorker(meerkatDBs.get(0), msg, MAX_RETRIES); + SingleServerPostMessageWorker worker = new SingleServerPostMessageWorker(dbAddress, msg, MAX_RETRIES); // Submit worker and create callback scheduleWorker(worker, new RetryCallback<>(worker, callback)); // Calculate the correct message ID and return it - batchDigest.reset(); - batchDigest.update(msg.getMsg()); - return batchDigest.digestAsMessageID(); + digest.reset(); + digest.update(msg.getMsg()); + return digest.digestAsMessageID(); } private class PostBatchDataCallback implements FutureCallback { - private final CompleteBatch completeBatch; + private final BulletinBoardMessage msg; + private final BatchIdentifier identifier; private final FutureCallback callback; - public PostBatchDataCallback(CompleteBatch completeBatch, FutureCallback callback) { - this.completeBatch = completeBatch; + public PostBatchDataCallback(BulletinBoardMessage msg, BatchIdentifier identifier, FutureCallback callback) { + this.msg = msg; + this.identifier = identifier; this.callback = callback; } @Override - public void onSuccess(Boolean msg) { + public void onSuccess(Boolean result) { closeBatch( - completeBatch.getCloseBatchMessage(), + identifier, + msg.getMsg().getTimestamp(), + msg.getSigList(), callback ); } @Override public void onFailure(Throwable t) { - callback.onFailure(t); + if (callback != null) + callback.onFailure(t); } } - private class BeginBatchCallback implements FutureCallback { + private class ContinueBatchCallback implements FutureCallback { - private final CompleteBatch completeBatch; + private final BulletinBoardMessage msg; + private final int chunkSize; private final FutureCallback callback; - public BeginBatchCallback(CompleteBatch completeBatch, FutureCallback callback) { - this.completeBatch = completeBatch; + public ContinueBatchCallback(BulletinBoardMessage msg, int chunkSize, FutureCallback callback) { + this.msg = msg; + this.chunkSize = chunkSize; this.callback = callback; } @Override - public void onSuccess(Boolean msg) { + public void onSuccess(BatchIdentifier identifier) { + + List batchChunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize); postBatchData( - completeBatch.getBeginBatchMessage().getSignerId(), - completeBatch.getBeginBatchMessage().getBatchId(), - completeBatch.getBatchDataList(), + identifier, + batchChunkList, 0, - new PostBatchDataCallback(completeBatch,callback)); + new PostBatchDataCallback(msg, identifier, callback)); + } + + @Override + public void onFailure(Throwable t) { + if (callback != null) + callback.onFailure(t); + } + } + + @Override + public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize, FutureCallback callback) { + + beginBatch( + msg.getMsg().getTagList(), + new ContinueBatchCallback(msg, chunkSize, callback) + ); + + digest.update(msg); + + return digest.digestAsMessageID(); + + } + + private class BeginBatchCallback implements FutureCallback { + + private final FutureCallback callback; + + public BeginBatchCallback(FutureCallback callback) { + this.callback = callback; + } + + @Override + public void onSuccess(Int64Value result) { + callback.onSuccess(new SingleServerBatchIdentifier(result)); } @Override @@ -430,51 +667,53 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i } @Override - public MessageID postBatch(CompleteBatch completeBatch, FutureCallback callback) { + public void beginBatch(Iterable tags, FutureCallback callback) { - beginBatch( - completeBatch.getBeginBatchMessage(), - new BeginBatchCallback(completeBatch, callback) - ); - - batchDigest.update(completeBatch); - - return batchDigest.digestAsMessageID(); - - } - - @Override - public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback callback) { + BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder() + .addAllTag(tags) + .build(); // Create worker with redundancy 1 and MAX_RETRIES retries SingleServerBeginBatchWorker worker = - new SingleServerBeginBatchWorker(meerkatDBs.get(0), beginBatchMessage, MAX_RETRIES); + new SingleServerBeginBatchWorker(dbAddress, beginBatchMessage, MAX_RETRIES); // Submit worker and create callback - scheduleWorker(worker, new RetryCallback<>(worker, callback)); + scheduleWorker(worker, new RetryCallback<>(worker, new BeginBatchCallback(callback))); } - @Override - public void postBatchData(ByteString signerId, int batchId, List batchDataList, - int startPosition, FutureCallback callback) { - BatchMessage.Builder builder = BatchMessage.newBuilder() - .setSignerId(signerId) - .setBatchId(batchId); + @Override + public void postBatchData(BatchIdentifier batchIdentifier, List batchChunkList, + int startPosition, FutureCallback callback) throws IllegalArgumentException{ + + // Cast identifier to usable form + + if (!(batchIdentifier instanceof SingleServerBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + SingleServerBatchIdentifier identifier = (SingleServerBatchIdentifier) batchIdentifier; + + // Update batch size + + identifier.setLength(startPosition + batchChunkList.size()); // Create a unified callback to aggregate successful posts - PostBatchDataListCallback listCallback = new PostBatchDataListCallback(batchDataList.size(), callback); + PostBatchChunkListCallback listCallback = new PostBatchChunkListCallback(batchChunkList.size(), callback); // Iterate through data list - for (BatchData data : batchDataList) { + BatchMessage.Builder builder = BatchMessage.newBuilder() + .setBatchId(identifier.getBatchId().getValue()); + + for (BatchChunk data : batchChunkList) { builder.setSerialNum(startPosition).setData(data); // Create worker with redundancy 1 and MAX_RETRIES retries SingleServerPostBatchWorker worker = - new SingleServerPostBatchWorker(meerkatDBs.get(0), builder.build(), MAX_RETRIES); + new SingleServerPostBatchWorker(dbAddress, builder.build(), MAX_RETRIES); // Create worker with redundancy 1 and MAX_RETRIES retries scheduleWorker(worker, new RetryCallback<>(worker, listCallback)); @@ -486,33 +725,33 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i } @Override - public void postBatchData(ByteString signerId, int batchId, List batchDataList, FutureCallback callback) { + public void postBatchData(BatchIdentifier batchIdentifier, List batchChunkList, FutureCallback callback) + throws IllegalArgumentException { - postBatchData(signerId, batchId, batchDataList, 0, callback); + postBatchData(batchIdentifier, batchChunkList, 0, callback); } @Override - public void postBatchData(byte[] signerId, int batchId, List batchDataList, - int startPosition, FutureCallback callback) { + public void closeBatch(BatchIdentifier batchIdentifier, Timestamp timestamp, Iterable signatures, FutureCallback callback) + throws IllegalArgumentException { - postBatchData(ByteString.copyFrom(signerId), batchId, batchDataList, startPosition, callback); + if (!(batchIdentifier instanceof SingleServerBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } - } + SingleServerBatchIdentifier identifier = (SingleServerBatchIdentifier) batchIdentifier; - @Override - public void postBatchData(byte[] signerId, int batchId, List batchDataList, FutureCallback callback) { - - postBatchData(signerId, batchId, batchDataList, 0, callback); - - } - - @Override - public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback callback) { + CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder() + .setBatchId(identifier.getBatchId().getValue()) + .setBatchLength(identifier.getLength()) + .setTimestamp(timestamp) + .addAllSig(signatures) + .build(); // Create worker with redundancy 1 and MAX_RETRIES retries SingleServerCloseBatchWorker worker = - new SingleServerCloseBatchWorker(meerkatDBs.get(0), closeBatchMessage, MAX_RETRIES); + new SingleServerCloseBatchWorker(dbAddress, closeBatchMessage, MAX_RETRIES); // Submit worker and create callback scheduleWorker(worker, new RetryCallback<>(worker, callback)); @@ -523,7 +762,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i public void getRedundancy(MessageID id, FutureCallback callback) { // Create worker with no retries - SingleServerGetRedundancyWorker worker = new SingleServerGetRedundancyWorker(meerkatDBs.get(0), id, 1); + SingleServerGetRedundancyWorker worker = new SingleServerGetRedundancyWorker(dbAddress, id, 1); // Submit job and create callback scheduleWorker(worker, new RetryCallback<>(worker, callback)); @@ -534,7 +773,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i public void readMessages(MessageFilterList filterList, FutureCallback> callback) { // Create job with no retries - SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(meerkatDBs.get(0), filterList, 1); + SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(dbAddress, filterList, 1); // Submit job and create callback scheduleWorker(worker, new RetryCallback<>(worker, callback)); @@ -542,43 +781,55 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i } @Override - public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback callback) { + public void readMessage(MessageID msgID, FutureCallback callback) { - // Create job with no retries for retrieval of the Bulletin Board Message that defines the batch + // Create job with MAX retries for retrieval of the Bulletin Board Message (which may be a stub) MessageFilterList filterList = MessageFilterList.newBuilder() .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(BulletinBoardConstants.BATCH_TAG) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(BulletinBoardConstants.BATCH_ID_TAG_PREFIX + batchSpecificationMessage.getBatchId()) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.SIGNER_ID) - .setId(batchSpecificationMessage.getSignerId()) + .setType(FilterType.MSG_ID) + .setId(msgID.getID()) .build()) .build(); - SingleServerReadMessagesWorker messageWorker = new SingleServerReadMessagesWorker(meerkatDBs.get(0), filterList, 1); + BatchQuery batchQuery = BatchQuery.newBuilder() + .setMsgID(msgID) + .setStartPosition(0) + .build(); - // Create job with no retries for retrieval of the Batch Data List - SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(meerkatDBs.get(0), batchSpecificationMessage, 1); - - // Create callback that will combine the two worker products - CompleteBatchReadCallback completeBatchReadCallback = new CompleteBatchReadCallback(callback); + SingleServerReadMessagesWorker messageWorker = new SingleServerReadMessagesWorker(dbAddress, filterList, MAX_RETRIES); // Submit jobs with wrapped callbacks - scheduleWorker(messageWorker, new RetryCallback<>(messageWorker, completeBatchReadCallback.asBulletinBoardMessageListFutureCallback())); - scheduleWorker(batchWorker, new RetryCallback<>(batchWorker, completeBatchReadCallback.asBatchDataListFutureCallback())); + scheduleWorker(messageWorker, new RetryCallback<>(messageWorker, new CompleteMessageReadCallback(callback))); + + } + + @Override + public void readBatchData(BulletinBoardMessage stub, FutureCallback callback) throws IllegalArgumentException{ + + if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { + throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); + } + + // Create job with MAX retries for retrieval of the Batch Data List + + BatchQuery batchQuery = BatchQuery.newBuilder() + .setMsgID(MessageID.newBuilder() + .setID(stub.getMsg().getMsgId()) + .build()) + .setStartPosition(0) + .build(); + + SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(dbAddress, batchQuery, MAX_RETRIES); + + scheduleWorker(batchWorker, new RetryCallback<>(batchWorker, new ReadBatchCallback(stub, callback))); } @Override public void querySync(SyncQuery syncQuery, FutureCallback callback) { - SingleServerQuerySyncWorker worker = new SingleServerQuerySyncWorker(meerkatDBs.get(0), syncQuery, MAX_RETRIES); + SingleServerQuerySyncWorker worker = new SingleServerQuerySyncWorker(dbAddress, syncQuery, MAX_RETRIES); scheduleWorker(worker, new RetryCallback<>(worker, callback)); @@ -604,7 +855,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i .build()); // Create job with no retries - SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(meerkatDBs.get(0), filterListBuilder.build(), MAX_RETRIES); + SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(dbAddress, filterListBuilder.build(), MAX_RETRIES); // Submit job and create callback that retries on failure and handles repeated subscription scheduleWorker(worker, new RetryCallback<>(worker, new SubscriptionCallback(worker, callback))); @@ -618,8 +869,6 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i @Override public void close() { - super.close(); - executorService.shutdown(); } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java index 2069859..9dd85b9 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java @@ -1,255 +1,286 @@ -package meerkat.bulletinboard; - -import com.google.common.util.concurrent.FutureCallback; -import com.google.protobuf.ByteString; - -import meerkat.bulletinboard.workers.multiserver.*; -import meerkat.comm.CommunicationException; -import meerkat.protobuf.BulletinBoardAPI; -import meerkat.protobuf.BulletinBoardAPI.*; -import meerkat.protobuf.Voting.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -/** - * Created by Arbel Deutsch Peled on 05-Dec-15. - * Thread-based implementation of a Async Bulletin Board Client. - * Features: - * 1. Handles tasks concurrently. - * 2. Retries submitting - */ -public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient implements AsyncBulletinBoardClient { - - // Executor service for handling jobs - private final static int JOBS_THREAD_NUM = 5; - private ExecutorService executorService; - - // Per-server clients - private List clients; - - private BatchDigest batchDigest; - - private final static int POST_MESSAGE_RETRY_NUM = 3; - private final static int READ_MESSAGES_RETRY_NUM = 1; - private final static int GET_REDUNDANCY_RETRY_NUM = 1; - - private static final int SERVER_THREADPOOL_SIZE = 5; - private static final long FAIL_DELAY = 5000; - private static final long SUBSCRIPTION_INTERVAL = 10000; - - private int minAbsoluteRedundancy; - - /** - * Stores database locations and initializes the web Client - * Stores the required minimum redundancy. - * Starts the Thread Pool. - * @param clientParams contains the required information - */ - @Override - public void init(BulletinBoardClientParams clientParams) { - - super.init(clientParams); - - batchDigest = new GenericBatchDigest(digest); - - minAbsoluteRedundancy = (int) (clientParams.getMinRedundancy() * (float) clientParams.getBulletinBoardAddressCount()); - - executorService = Executors.newFixedThreadPool(JOBS_THREAD_NUM); - - clients = new ArrayList<>(clientParams.getBulletinBoardAddressCount()); - for (String address : clientParams.getBulletinBoardAddressList()){ - - SingleServerBulletinBoardClient client = - new SingleServerBulletinBoardClient(SERVER_THREADPOOL_SIZE, FAIL_DELAY, SUBSCRIPTION_INTERVAL); - - client.init(BulletinBoardClientParams.newBuilder() - .addBulletinBoardAddress(address) - .build()); - - clients.add(client); - - } - - } - - /** - * Post message to all DBs - * Retry failed DBs - * @param msg is the message, - * @return the message ID for later retrieval - */ - @Override - public MessageID postMessage(BulletinBoardMessage msg, FutureCallback callback){ - - // Create job - MultiServerPostMessageWorker worker = - new MultiServerPostMessageWorker(clients, minAbsoluteRedundancy, msg, POST_MESSAGE_RETRY_NUM, callback); - - // Submit job - executorService.submit(worker); - - // Calculate the correct message ID and return it - batchDigest.reset(); - batchDigest.update(msg.getMsg()); - return batchDigest.digestAsMessageID(); - - } - - @Override - public MessageID postBatch(CompleteBatch completeBatch, FutureCallback callback) { - - // Create job - MultiServerPostBatchWorker worker = - new MultiServerPostBatchWorker(clients, minAbsoluteRedundancy, completeBatch, POST_MESSAGE_RETRY_NUM, callback); - - // Submit job - executorService.submit(worker); - - // Calculate the correct message ID and return it - batchDigest.reset(); - batchDigest.update(completeBatch); - return batchDigest.digestAsMessageID(); - - } - - @Override - public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback callback) { - - // Create job - MultiServerBeginBatchWorker worker = - new MultiServerBeginBatchWorker(clients, minAbsoluteRedundancy, beginBatchMessage, POST_MESSAGE_RETRY_NUM, callback); - - // Submit job - executorService.submit(worker); - - } - - @Override - public void postBatchData(byte[] signerId, int batchId, List batchDataList, - int startPosition, FutureCallback callback) { - - BatchDataContainer batchDataContainer = new BatchDataContainer(signerId, batchId, batchDataList, startPosition); - - // Create job - MultiServerPostBatchDataWorker worker = - new MultiServerPostBatchDataWorker(clients, minAbsoluteRedundancy, batchDataContainer, POST_MESSAGE_RETRY_NUM, callback); - - // Submit job - executorService.submit(worker); - - } - - @Override - public void postBatchData(byte[] signerId, int batchId, List batchDataList, FutureCallback callback) { - - postBatchData(signerId, batchId, batchDataList, 0, callback); - - } - - @Override - public void postBatchData(ByteString signerId, int batchId, List batchDataList, - int startPosition, FutureCallback callback) { - - postBatchData(signerId.toByteArray(), batchId, batchDataList, startPosition, callback); - - } - - @Override - public void postBatchData(ByteString signerId, int batchId, List batchDataList, FutureCallback callback) { - - postBatchData(signerId, batchId, batchDataList, 0, callback); - - } - - @Override - public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback callback) { - - // Create job - MultiServerCloseBatchWorker worker = - new MultiServerCloseBatchWorker(clients, minAbsoluteRedundancy, closeBatchMessage, POST_MESSAGE_RETRY_NUM, callback); - - // Submit job - executorService.submit(worker); - - } - - /** - * Access each database and search for a given message ID - * Return the number of databases in which the message was found - * Only try once per DB - * Ignore communication exceptions in specific databases - */ - @Override - public void getRedundancy(MessageID id, FutureCallback callback) { - - // Create job - MultiServerGetRedundancyWorker worker = - new MultiServerGetRedundancyWorker(clients, minAbsoluteRedundancy, id, GET_REDUNDANCY_RETRY_NUM, callback); - - // Submit job - executorService.submit(worker); - - } - - /** - * Go through the DBs and try to retrieve messages according to the specified filter - * If at the operation is successful for some DB: return the results and stop iterating - * If no operation is successful: return null (NOT blank list) - */ - @Override - public void readMessages(MessageFilterList filterList, FutureCallback> callback) { - - // Create job - MultiServerReadMessagesWorker worker = - new MultiServerReadMessagesWorker(clients, minAbsoluteRedundancy, filterList, READ_MESSAGES_RETRY_NUM, callback); - - // Submit job - executorService.submit(worker); - - } - - @Override - public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback callback) { - - // Create job - MultiServerReadBatchWorker worker = - new MultiServerReadBatchWorker(clients, minAbsoluteRedundancy, batchSpecificationMessage, READ_MESSAGES_RETRY_NUM, callback); - - // Submit job - executorService.submit(worker); - - } - - /** - * This method is not supported by this class! - * This is because it has no meaning when considering more than one server without knowing which server will be contacted - */ - @Override - public void querySync(SyncQuery syncQuery, FutureCallback callback) { - callback.onFailure(new IllegalAccessError("querySync is not supported by this class")); - } - - @Override - public void close() { - super.close(); - - try { - - for (SingleServerBulletinBoardClient client : clients){ - client.close(); - } - - executorService.shutdown(); - while (! executorService.isShutdown()) { - executorService.awaitTermination(10, TimeUnit.SECONDS); - } - } catch (InterruptedException e) { - System.err.println(e.getCause() + " " + e.getMessage()); - } - } - -} +package meerkat.bulletinboard; + +import com.google.common.util.concurrent.FutureCallback; + +import com.google.protobuf.Timestamp; +import meerkat.bulletinboard.workers.multiserver.*; +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Crypto.Signature; +import meerkat.protobuf.Voting.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * Created by Arbel Deutsch Peled on 05-Dec-15. + * Thread-based implementation of a Async Bulletin Board Client. + * Features: + * 1. Handles tasks concurrently. + * 2. Retries submitting + */ +public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient implements AsyncBulletinBoardClient { + + // Executor service for handling jobs + private final static int JOBS_THREAD_NUM = 5; + private ExecutorService executorService; + + // Per-server clients + private List clients; + + private BulletinBoardDigest batchDigest; + + private final static int POST_MESSAGE_RETRY_NUM = 3; + private final static int READ_MESSAGES_RETRY_NUM = 1; + private final static int GET_REDUNDANCY_RETRY_NUM = 1; + + private final int SERVER_THREADPOOL_SIZE; + private final long FAIL_DELAY; + private final long SUBSCRIPTION_INTERVAL; + + private static final int DEFAULT_SERVER_THREADPOOL_SIZE = 5; + private static final long DEFAULT_FAIL_DELAY = 5000; + private static final long DEFAULT_SUBSCRIPTION_INTERVAL = 10000; + + private int minAbsoluteRedundancy; + + + public ThreadedBulletinBoardClient(int serverThreadpoolSize, long failDelay, long subscriptionInterval) { + SERVER_THREADPOOL_SIZE = serverThreadpoolSize; + FAIL_DELAY = failDelay; + SUBSCRIPTION_INTERVAL = subscriptionInterval; + } + + public ThreadedBulletinBoardClient() { + this(DEFAULT_SERVER_THREADPOOL_SIZE, DEFAULT_FAIL_DELAY, DEFAULT_SUBSCRIPTION_INTERVAL); + } + + /** + * Stores database locations and initializes the web Client + * Stores the required minimum redundancy. + * Starts the Thread Pool. + * @param clientParams contains the required information + */ + @Override + public void init(BulletinBoardClientParams clientParams) { + + super.init(clientParams); + + batchDigest = new GenericBulletinBoardDigest(digest); + + minAbsoluteRedundancy = (int) (clientParams.getMinRedundancy() * (float) clientParams.getBulletinBoardAddressCount()); + + executorService = Executors.newFixedThreadPool(JOBS_THREAD_NUM); + + clients = new ArrayList<>(clientParams.getBulletinBoardAddressCount()); + for (String address : clientParams.getBulletinBoardAddressList()){ + + SingleServerBulletinBoardClient client = + new SingleServerBulletinBoardClient(SERVER_THREADPOOL_SIZE, FAIL_DELAY, SUBSCRIPTION_INTERVAL); + + client.init(BulletinBoardClientParams.newBuilder() + .addBulletinBoardAddress(address) + .build()); + + clients.add(client); + + } + + } + + /** + * Post message to all DBs + * Retry failed DBs + * @param msg is the message, + * @return the message ID for later retrieval + */ + @Override + public MessageID postMessage(BulletinBoardMessage msg, FutureCallback callback){ + + // Create job + MultiServerPostMessageWorker worker = + new MultiServerPostMessageWorker(clients, minAbsoluteRedundancy, msg, POST_MESSAGE_RETRY_NUM, callback); + + // Submit job + executorService.submit(worker); + + // Calculate the correct message ID and return it + batchDigest.reset(); + batchDigest.update(msg.getMsg()); + return batchDigest.digestAsMessageID(); + + } + + @Override + public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize, FutureCallback callback) { + + // Create job + MultiServerPostBatchWorker worker = + new MultiServerPostBatchWorker(clients, minAbsoluteRedundancy, msg, chunkSize, POST_MESSAGE_RETRY_NUM, callback); + + // Submit job + executorService.submit(worker); + + // Calculate the correct message ID and return it + batchDigest.reset(); + batchDigest.update(msg); + return batchDigest.digestAsMessageID(); + + } + + @Override + public void beginBatch(Iterable tags, FutureCallback callback) { + + // Create job + MultiServerBeginBatchWorker worker = + new MultiServerBeginBatchWorker(clients, minAbsoluteRedundancy, tags, POST_MESSAGE_RETRY_NUM, callback); + + // Submit job + executorService.submit(worker); + + } + + @Override + public void postBatchData(BatchIdentifier batchIdentifier, List batchChunkList, + int startPosition, FutureCallback callback) throws IllegalArgumentException { + + // Cast identifier to usable form + + if (!(batchIdentifier instanceof MultiServerBatchIdentifier)){ + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + MultiServerBatchIdentifier identifier = (MultiServerBatchIdentifier) batchIdentifier; + + BatchDataContainer batchDataContainer = new BatchDataContainer(identifier, batchChunkList, startPosition); + + // Create job + MultiServerPostBatchDataWorker worker = + new MultiServerPostBatchDataWorker(clients, minAbsoluteRedundancy, batchDataContainer, POST_MESSAGE_RETRY_NUM, callback); + + // Submit job + executorService.submit(worker); + + } + + @Override + public void postBatchData(BatchIdentifier batchIdentifier, List batchChunkList, FutureCallback callback) + throws IllegalArgumentException { + + postBatchData(batchIdentifier, batchChunkList, 0, callback); + + } + + @Override + public void closeBatch(BatchIdentifier payload, Timestamp timestamp, Iterable signatures, FutureCallback callback) + throws IllegalArgumentException{ + + if (!(payload instanceof MultiServerBatchIdentifier)) { + throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); + } + + MultiServerBatchIdentifier identifier = (MultiServerBatchIdentifier) payload; + + // Create job + MultiServerCloseBatchWorker worker = + new MultiServerCloseBatchWorker(clients, minAbsoluteRedundancy, identifier, timestamp, signatures, POST_MESSAGE_RETRY_NUM, callback); + + // Submit job + executorService.submit(worker); + + } + + /** + * Access each database and search for a given message ID + * Return the number of databases in which the message was found + * Only try once per DB + * Ignore communication exceptions in specific databases + */ + @Override + public void getRedundancy(MessageID id, FutureCallback callback) { + + // Create job + MultiServerGetRedundancyWorker worker = + new MultiServerGetRedundancyWorker(clients, minAbsoluteRedundancy, id, GET_REDUNDANCY_RETRY_NUM, callback); + + // Submit job + executorService.submit(worker); + + } + + /** + * Go through the DBs and try to retrieve messages according to the specified filter + * If at the operation is successful for some DB: return the results and stop iterating + * If no operation is successful: return null (NOT blank list) + */ + @Override + public void readMessages(MessageFilterList filterList, FutureCallback> callback) { + + // Create job + MultiServerReadMessagesWorker worker = + new MultiServerReadMessagesWorker(clients, minAbsoluteRedundancy, filterList, READ_MESSAGES_RETRY_NUM, callback); + + // Submit job + executorService.submit(worker); + + } + + @Override + public void readMessage(MessageID msgID, FutureCallback callback) { + + //Create job + MultiServerReadMessageWorker worker = + new MultiServerReadMessageWorker(clients, minAbsoluteRedundancy, msgID, READ_MESSAGES_RETRY_NUM, callback); + + // Submit job + executorService.submit(worker); + + } + + @Override + public void readBatchData(BulletinBoardMessage stub, FutureCallback callback) throws IllegalArgumentException { + + if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { + throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); + } + + // Create job + MultiServerReadBatchDataWorker worker = + new MultiServerReadBatchDataWorker(clients, minAbsoluteRedundancy, stub, READ_MESSAGES_RETRY_NUM, callback); + + // Submit job + executorService.submit(worker); + + } + + /** + * This method is not supported by this class! + * This is because it has no meaning when considering more than one server without knowing which server will be contacted + */ + @Override + public void querySync(SyncQuery syncQuery, FutureCallback callback) { + callback.onFailure(new IllegalAccessError("querySync is not supported by this class")); + } + + @Override + public void close() { + super.close(); + + try { + + for (SingleServerBulletinBoardClient client : clients){ + client.close(); + } + + executorService.shutdown(); + while (! executorService.isShutdown()) { + executorService.awaitTermination(10, TimeUnit.SECONDS); + } + } catch (InterruptedException e) { + System.err.println(e.getCause() + " " + e.getMessage()); + } + } + +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java index cf8d47d..252d6e3 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardSubscriber.java @@ -8,7 +8,6 @@ import meerkat.util.BulletinBoardUtils; import static meerkat.protobuf.BulletinBoardAPI.FilterType.*; -import java.sql.Time; import java.util.*; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; @@ -19,20 +18,22 @@ import java.util.concurrent.atomic.AtomicBoolean; */ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber { - protected final Collection clients; + protected final Collection clients; protected final BulletinBoardClient localClient; - protected Iterator clientIterator; - protected SubscriptionAsyncBulletinBoardClient currentClient; + protected Iterator clientIterator; + protected SubscriptionBulletinBoardClient currentClient; private long lastServerSwitchTime; private AtomicBoolean isSyncInProgress; private Semaphore rescheduleSemaphore; + private AtomicBoolean stopped; + private static final Float[] BREAKPOINTS = {0.5f, 0.75f, 0.9f, 0.95f, 0.99f, 0.999f}; - public ThreadedBulletinBoardSubscriber(Collection clients, BulletinBoardClient localClient) { + public ThreadedBulletinBoardSubscriber(Collection clients, BulletinBoardClient localClient) { this.clients = clients; this.localClient = localClient; @@ -45,6 +46,8 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber isSyncInProgress = new AtomicBoolean(false); rescheduleSemaphore = new Semaphore(1); + stopped = new AtomicBoolean(false); + } /** @@ -132,7 +135,8 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber //TODO: log - callback.onFailure(e); // Hard error: Cannot guarantee subscription safety + if (callback != null) + callback.onFailure(e); // Hard error: Cannot guarantee subscription safety } @@ -218,7 +222,8 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber public void onSuccess(List result) { // Propagate result to caller - callback.onSuccess(result); + if (callback != null) + callback.onSuccess(result); // Renew subscription @@ -245,12 +250,12 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber super(filterList, callback); } - @Override public void onSuccess(List result) { // Propagate result to caller - callback.onSuccess(result); + if (callback != null) + callback.onSuccess(result); } @@ -268,5 +273,4 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber subscribe(filterList, 0, callback); } - } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerBeginBatchWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerBeginBatchWorker.java index e0e92bb..7d64946 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerBeginBatchWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerBeginBatchWorker.java @@ -1,28 +1,96 @@ package meerkat.bulletinboard.workers.multiserver; import com.google.common.util.concurrent.FutureCallback; +import meerkat.bulletinboard.MultiServerBatchIdentifier; +import meerkat.bulletinboard.MultiServerWorker; import meerkat.bulletinboard.SingleServerBulletinBoardClient; -import meerkat.protobuf.BulletinBoardAPI.BeginBatchMessage; +import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; +import meerkat.comm.CommunicationException; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; /** * Created by Arbel Deutsch Peled on 27-Dec-15. */ -public class MultiServerBeginBatchWorker extends MultiServerGenericPostWorker { +public class MultiServerBeginBatchWorker extends MultiServerWorker, BatchIdentifier> { + + private BatchIdentifier[] identifiers; + private AtomicInteger remainingServers; public MultiServerBeginBatchWorker(List clients, - int minServers, BeginBatchMessage payload, int maxRetry, - FutureCallback futureCallback) { + int minServers, Iterable payload, int maxRetry, + FutureCallback futureCallback) { super(clients, minServers, payload, maxRetry, futureCallback); + identifiers = new BatchIdentifier[clients.size()]; + + for (int i = 0 ; i < identifiers.length ; i++) { + identifiers[i] = null; + } + + remainingServers = new AtomicInteger(clients.size()); + + } + + private class BeginBatchCallback implements FutureCallback { + + private final int clientNum; + + public BeginBatchCallback(int clientNum) { + this.clientNum = clientNum; + } + + private void finishPost() { + + if (remainingServers.decrementAndGet() <= 0){ + + if (minServers.decrementAndGet() <= 0) { + MultiServerBeginBatchWorker.this.onSuccess(new MultiServerBatchIdentifier(identifiers)); + } else { + MultiServerBeginBatchWorker.this.onFailure(new CommunicationException("Could not open batch in enough servers")); + } + } + + } + + @Override + public void onSuccess(BatchIdentifier result) { + + identifiers[clientNum] = result; + finishPost(); + + } + + @Override + public void onFailure(Throwable t) { + finishPost(); + } } @Override - protected void doPost(SingleServerBulletinBoardClient client, BeginBatchMessage payload) { - client.beginBatch(payload, this); + public void onSuccess(BatchIdentifier result) { + succeed(result); } + @Override + public void onFailure(Throwable t) { + fail(t); + } + @Override + public void run() { + + int clientNum = 0; + + for (SingleServerBulletinBoardClient client : clients){ + + client.beginBatch(payload, new BeginBatchCallback(clientNum)); + + clientNum++; + + } + + } } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerCloseBatchWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerCloseBatchWorker.java index 300440f..1e5a910 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerCloseBatchWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerCloseBatchWorker.java @@ -1,27 +1,83 @@ package meerkat.bulletinboard.workers.multiserver; import com.google.common.util.concurrent.FutureCallback; +import com.google.protobuf.Timestamp; +import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; +import meerkat.bulletinboard.BatchDataContainer; +import meerkat.bulletinboard.MultiServerBatchIdentifier; +import meerkat.bulletinboard.MultiServerWorker; import meerkat.bulletinboard.SingleServerBulletinBoardClient; -import meerkat.protobuf.BulletinBoardAPI.CloseBatchMessage; +import meerkat.crypto.DigitalSignature; +import meerkat.protobuf.Crypto; +import meerkat.protobuf.Crypto.Signature; +import java.util.Iterator; import java.util.List; /** * Created by Arbel Deutsch Peled on 27-Dec-15. */ -public class MultiServerCloseBatchWorker extends MultiServerGenericPostWorker { +public class MultiServerCloseBatchWorker extends MultiServerWorker { + + private final Timestamp timestamp; + private final Iterable signatures; public MultiServerCloseBatchWorker(List clients, - int minServers, CloseBatchMessage payload, int maxRetry, - FutureCallback futureCallback) { + int minServers, MultiServerBatchIdentifier payload, Timestamp timestamp, Iterable signatures, + int maxRetry, FutureCallback futureCallback) { super(clients, minServers, payload, maxRetry, futureCallback); + this.timestamp = timestamp; + this.signatures = signatures; + } @Override - protected void doPost(SingleServerBulletinBoardClient client, CloseBatchMessage payload) { - client.closeBatch(payload, this); + public void run() { + + Iterator identifierIterator = payload.getIdentifiers().iterator(); + + // Iterate through client + + for (SingleServerBulletinBoardClient client : clients) { + + if (identifierIterator.hasNext()) { + + // Fetch the batch identifier supplied by the specific client (may be null if batch open failed on client + + BatchIdentifier identifier = identifierIterator.next(); + + if (identifier != null) { + + // Post the data with the matching identifier to the client + client.closeBatch(identifier, timestamp, signatures, this); + + } else { + + // Count servers with no batch identifier as failed + maxFailedServers.decrementAndGet(); + + } + + } + + } + + } + + @Override + public void onSuccess(Boolean result) { + if (minServers.decrementAndGet() <= 0){ + succeed(result); + } + } + + @Override + public void onFailure(Throwable t) { + if (maxFailedServers.decrementAndGet() <= 0){ + fail(t); + } } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericPostWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericPostWorker.java index 8172e14..a720eda 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericPostWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericPostWorker.java @@ -35,14 +35,9 @@ public abstract class MultiServerGenericPostWorker extends MultiServerWorker< public void run() { // Iterate through servers - - Iterator clientIterator = getClientIterator(); - - while (clientIterator.hasNext()) { + for (SingleServerBulletinBoardClient client : clients) { // Send request to Server - SingleServerBulletinBoardClient client = clientIterator.next(); - doPost(client, payload); } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericReadWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericReadWorker.java index 88b4ac1..f17708b 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericReadWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGenericReadWorker.java @@ -14,7 +14,9 @@ import java.util.List; */ public abstract class MultiServerGenericReadWorker extends MultiServerWorker{ - private final Iterator clientIterator; + private Iterator clientIterator; + + private String errorString; public MultiServerGenericReadWorker(List clients, int minServers, IN payload, int maxRetry, @@ -22,7 +24,8 @@ public abstract class MultiServerGenericReadWorker extends MultiServerW super(clients, true, minServers, payload, maxRetry, futureCallback); // Shuffle clients on creation to balance load - clientIterator = getClientIterator(); + clientIterator = clients.iterator(); + errorString = ""; } @@ -46,7 +49,7 @@ public abstract class MultiServerGenericReadWorker extends MultiServerW doRead(payload, client); } else { - fail(new CommunicationException("Could not contact any server")); + fail(new CommunicationException("Could not contact any server. Errors follow:\n" + errorString)); } } @@ -58,6 +61,8 @@ public abstract class MultiServerGenericReadWorker extends MultiServerW @Override public void onFailure(Throwable t) { + //TODO: log + errorString += t.getCause() + " " + t.getMessage() + "\n"; run(); // Retry with next server } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGetRedundancyWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGetRedundancyWorker.java index 748916b..b76a94f 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGetRedundancyWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerGetRedundancyWorker.java @@ -36,13 +36,8 @@ public class MultiServerGetRedundancyWorker extends MultiServerWorker clientIterator = getClientIterator(); - // Iterate through clients - - while (clientIterator.hasNext()) { - - SingleServerBulletinBoardClient client = clientIterator.next(); + for (SingleServerBulletinBoardClient client : clients) { // Send request to client client.getRedundancy(payload,this); diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchDataWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchDataWorker.java index ef1c4fa..8ef5931 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchDataWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchDataWorker.java @@ -1,15 +1,18 @@ package meerkat.bulletinboard.workers.multiserver; import com.google.common.util.concurrent.FutureCallback; +import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; +import meerkat.bulletinboard.MultiServerWorker; import meerkat.bulletinboard.SingleServerBulletinBoardClient; import meerkat.bulletinboard.BatchDataContainer; +import java.util.Iterator; import java.util.List; /** * Created by Arbel Deutsch Peled on 27-Dec-15. */ -public class MultiServerPostBatchDataWorker extends MultiServerGenericPostWorker { +public class MultiServerPostBatchDataWorker extends MultiServerWorker { public MultiServerPostBatchDataWorker(List clients, int minServers, BatchDataContainer payload, int maxRetry, @@ -20,9 +23,50 @@ public class MultiServerPostBatchDataWorker extends MultiServerGenericPostWorker } @Override - protected void doPost(SingleServerBulletinBoardClient client, BatchDataContainer payload) { - client.postBatchData(payload.signerId, payload.batchId, payload.batchDataList, payload.startPosition, this); + public void run() { + + Iterator identifierIterator = payload.batchId.getIdentifiers().iterator(); + + // Iterate through client + + for (SingleServerBulletinBoardClient client : clients) { + + if (identifierIterator.hasNext()) { + + // Fetch the batch identifier supplied by the specific client (may be null if batch open failed on client + + BatchIdentifier identifier = identifierIterator.next(); + + if (identifier != null) { + + // Post the data with the matching identifier to the client + client.postBatchData(identifier, payload.batchChunkList, payload.startPosition, this); + + } else { + + // Count servers with no batch identifier as failed + maxFailedServers.decrementAndGet(); + + } + + } + + } + } + @Override + public void onSuccess(Boolean result) { + if (minServers.decrementAndGet() <= 0){ + succeed(result); + } + } + + @Override + public void onFailure(Throwable t) { + if (maxFailedServers.decrementAndGet() <= 0){ + fail(t); + } + } } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchWorker.java index 1b1f3df..b938f52 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerPostBatchWorker.java @@ -1,27 +1,33 @@ package meerkat.bulletinboard.workers.multiserver; import com.google.common.util.concurrent.FutureCallback; -import meerkat.bulletinboard.CompleteBatch; import meerkat.bulletinboard.SingleServerBulletinBoardClient; +import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage; import java.util.List; /** * Created by Arbel Deutsch Peled on 27-Dec-15. */ -public class MultiServerPostBatchWorker extends MultiServerGenericPostWorker { +public class MultiServerPostBatchWorker extends MultiServerGenericPostWorker { + + private final int chunkSize; public MultiServerPostBatchWorker(List clients, - int minServers, CompleteBatch payload, int maxRetry, + int minServers, BulletinBoardMessage payload, int chunkSize, int maxRetry, FutureCallback futureCallback) { super(clients, minServers, payload, maxRetry, futureCallback); + this.chunkSize = chunkSize; + } @Override - protected void doPost(SingleServerBulletinBoardClient client, CompleteBatch payload) { - client.postBatch(payload, this); + protected void doPost(SingleServerBulletinBoardClient client, BulletinBoardMessage payload) { + + client.postAsBatch(payload, chunkSize, this); + } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java new file mode 100644 index 0000000..769702a --- /dev/null +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchDataWorker.java @@ -0,0 +1,29 @@ +package meerkat.bulletinboard.workers.multiserver; + +import com.google.common.util.concurrent.FutureCallback; +import meerkat.bulletinboard.SingleServerBulletinBoardClient; +import meerkat.protobuf.BulletinBoardAPI.*; + +import java.util.List; + + +/** + * Created by Arbel Deutsch Peled on 27-Dec-15. + */ +public class MultiServerReadBatchDataWorker extends MultiServerGenericReadWorker { + + public MultiServerReadBatchDataWorker(List clients, + int minServers, BulletinBoardMessage payload, int maxRetry, + FutureCallback futureCallback) { + + super(clients, minServers, payload, maxRetry, futureCallback); + + } + + @Override + protected void doRead(BulletinBoardMessage payload, SingleServerBulletinBoardClient client) { + client.readBatchData(payload, this); + } + + +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchWorker.java deleted file mode 100644 index 3d40c8a..0000000 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadBatchWorker.java +++ /dev/null @@ -1,30 +0,0 @@ -package meerkat.bulletinboard.workers.multiserver; - -import com.google.common.util.concurrent.FutureCallback; -import meerkat.bulletinboard.CompleteBatch; -import meerkat.bulletinboard.SingleServerBulletinBoardClient; -import meerkat.protobuf.BulletinBoardAPI.BatchSpecificationMessage; - -import java.util.List; - - -/** - * Created by Arbel Deutsch Peled on 27-Dec-15. - */ -public class MultiServerReadBatchWorker extends MultiServerGenericReadWorker { - - public MultiServerReadBatchWorker(List clients, - int minServers, BatchSpecificationMessage payload, int maxRetry, - FutureCallback futureCallback) { - - super(clients, minServers, payload, maxRetry, futureCallback); - - } - - @Override - protected void doRead(BatchSpecificationMessage payload, SingleServerBulletinBoardClient client) { - client.readBatch(payload, this); - } - - -} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadMessageWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadMessageWorker.java new file mode 100644 index 0000000..f84d67e --- /dev/null +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/multiserver/MultiServerReadMessageWorker.java @@ -0,0 +1,30 @@ +package meerkat.bulletinboard.workers.multiserver; + +import com.google.common.util.concurrent.FutureCallback; +import meerkat.bulletinboard.SingleServerBulletinBoardClient; +import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage; +import meerkat.protobuf.BulletinBoardAPI.MessageID; + +import java.util.List; + + +/** + * Created by Arbel Deutsch Peled on 27-Dec-15. + */ +public class MultiServerReadMessageWorker extends MultiServerGenericReadWorker { + + public MultiServerReadMessageWorker(List clients, + int minServers, MessageID payload, int maxRetry, + FutureCallback futureCallback) { + + super(clients, minServers, payload, maxRetry, futureCallback); + + } + + @Override + protected void doRead(MessageID payload, SingleServerBulletinBoardClient client) { + client.readMessage(payload, this); + } + + +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerBeginBatchWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerBeginBatchWorker.java index 0c1a1a3..e08b360 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerBeginBatchWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerBeginBatchWorker.java @@ -1,17 +1,53 @@ package meerkat.bulletinboard.workers.singleserver; +import com.google.protobuf.Int64Value; +import meerkat.bulletinboard.SingleServerWorker; +import meerkat.comm.CommunicationException; import meerkat.protobuf.BulletinBoardAPI.BeginBatchMessage; +import meerkat.rest.Constants; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; import static meerkat.bulletinboard.BulletinBoardConstants.BEGIN_BATCH_PATH; +import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH; /** * Created by Arbel Deutsch Peled on 27-Dec-15. * Tries to contact server once and perform a post operation */ -public class SingleServerBeginBatchWorker extends SingleServerGenericPostWorker { +public class SingleServerBeginBatchWorker extends SingleServerWorker { public SingleServerBeginBatchWorker(String serverAddress, BeginBatchMessage payload, int maxRetry) { - super(serverAddress, BEGIN_BATCH_PATH, payload, maxRetry); + super(serverAddress, payload, maxRetry); } + @Override + public Int64Value call() throws Exception { + Client client = clientLocal.get(); + + WebTarget webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(BEGIN_BATCH_PATH); + Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post( + Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF)); + + try { + + Int64Value result = response.readEntity(Int64Value.class); + return result; + + } catch (ProcessingException | IllegalStateException e) { + + // Post to this server failed + throw new CommunicationException("Could not contact the server. Original error: " + e.getMessage()); + + } catch (Exception e) { + throw new CommunicationException("Could not contact the server. Original error: " + e.getMessage()); + } + finally { + response.close(); + } + } } diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGenerateSyncQueryWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGenerateSyncQueryWorker.java new file mode 100644 index 0000000..71d90e0 --- /dev/null +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGenerateSyncQueryWorker.java @@ -0,0 +1,55 @@ +package meerkat.bulletinboard.workers.singleserver; + +import com.google.protobuf.Int64Value; +import meerkat.bulletinboard.SingleServerWorker; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.BulletinBoardAPI.SyncQuery; +import meerkat.protobuf.BulletinBoardAPI.GenerateSyncQueryParams; +import meerkat.rest.Constants; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; + +import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH; +import static meerkat.bulletinboard.BulletinBoardConstants.GENERATE_SYNC_QUERY_PATH; + +/** + * Created by Arbel Deutsch Peled on 27-Dec-15. + * Tries to contact server once and perform a Sync Query Generation operation + */ +public class SingleServerGenerateSyncQueryWorker extends SingleServerWorker { + + public SingleServerGenerateSyncQueryWorker(String serverAddress, GenerateSyncQueryParams payload, int maxRetry) { + super(serverAddress, payload, maxRetry); + } + + @Override + public SyncQuery call() throws Exception { + + Client client = clientLocal.get(); + + WebTarget webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(GENERATE_SYNC_QUERY_PATH); + + Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF)); + + try { + + SyncQuery result = response.readEntity(SyncQuery.class); + return result; + + } catch (ProcessingException | IllegalStateException e) { + + // Post to this server failed + throw new CommunicationException("Could not contact the server. Original error: " + e.getMessage()); + + } catch (Exception e) { + throw new CommunicationException("Could not contact the server. Original error: " + e.getMessage()); + } + finally { + response.close(); + } + } +} diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGetRedundancyWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGetRedundancyWorker.java index 0401a76..23c07af 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGetRedundancyWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerGetRedundancyWorker.java @@ -6,7 +6,6 @@ import meerkat.comm.MessageInputStream; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.rest.Constants; -import javax.ws.rs.ProcessingException; import javax.ws.rs.client.Client; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; @@ -14,7 +13,6 @@ import javax.ws.rs.core.Response; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH; import static meerkat.bulletinboard.BulletinBoardConstants.READ_MESSAGES_PATH; diff --git a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerReadBatchWorker.java b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerReadBatchWorker.java index 57b336e..8bc4bcd 100644 --- a/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerReadBatchWorker.java +++ b/bulletin-board-client/src/main/java/meerkat/bulletinboard/workers/singleserver/SingleServerReadBatchWorker.java @@ -1,35 +1,28 @@ package meerkat.bulletinboard.workers.singleserver; -import meerkat.bulletinboard.CompleteBatch; import meerkat.bulletinboard.SingleServerWorker; import meerkat.comm.CommunicationException; 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; -import javax.ws.rs.core.GenericType; -import javax.ws.rs.core.Response; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.util.List; import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH; -import static meerkat.bulletinboard.BulletinBoardConstants.READ_MESSAGES_PATH; import static meerkat.bulletinboard.BulletinBoardConstants.READ_BATCH_PATH; -import static meerkat.bulletinboard.BulletinBoardConstants.BATCH_ID_TAG_PREFIX; - /** * Created by Arbel Deutsch Peled on 27-Dec-15. */ -public class SingleServerReadBatchWorker extends SingleServerWorker> { +public class SingleServerReadBatchWorker extends SingleServerWorker> { - public SingleServerReadBatchWorker(String serverAddress, BatchSpecificationMessage payload, int maxRetry) { + public SingleServerReadBatchWorker(String serverAddress, BatchQuery payload, int maxRetry) { super(serverAddress, payload, maxRetry); } @@ -39,7 +32,7 @@ public class SingleServerReadBatchWorker extends SingleServerWorker call() throws CommunicationException{ + public List call() throws CommunicationException{ Client client = clientLocal.get(); @@ -50,11 +43,11 @@ public class SingleServerReadBatchWorker extends SingleServerWorker inputStream = null; + MessageInputStream inputStream = null; try { - inputStream = MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BatchData.class); + inputStream = MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BatchChunk.class); return inputStream.asList(); diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java new file mode 100644 index 0000000..19e7ae5 --- /dev/null +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/BulletinBoardSynchronizerTest.java @@ -0,0 +1,318 @@ +package meerkat.bulletinboard; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.protobuf.*; +import com.google.protobuf.Timestamp; + +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 java.io.IOException; +import java.io.InputStream; +import java.security.*; +import java.security.cert.CertificateException; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.fail; + +/** + * 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 int testCount; + + private static final int THREAD_NUM = 3; + private static final int SUBSCRIPTION_INTERVAL = 1000; + + private static final int SYNC_SLEEP_INTERVAL = 500; + private static final int SYNC_WAIT_CAP = 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 BulletinBoardSignature[] signers; + private static ByteString[] signerIDs; + + private Semaphore semaphore; + private List thrown; + + @BeforeClass + public static void build() { + + messageGenerator = new BulletinBoardMessageGenerator(new Random(0)); + messageComparator = new BulletinBoardMessageComparator(); + + signers = new BulletinBoardSignature[1]; + signerIDs = new ByteString[1]; + + signers[0] = new GenericBulletinBoardSignature(new ECDSASignature()); + signerIDs[0] = signers[0].getSignerID(); + + 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(); + + testCount = 0; + + } + + @Before + public void init() throws CommunicationException { + + DeletableBulletinBoardServer remoteServer = new BulletinBoardSQLServer(new H2QueryProvider(REMOTE_SERVER_ADDRESS + testCount)); + remoteServer.init(); + + remoteClient = new LocalBulletinBoardClient( + remoteServer, + THREAD_NUM, + SUBSCRIPTION_INTERVAL); + + DeletableBulletinBoardServer localServer = new BulletinBoardSQLServer(new H2QueryProvider(LOCAL_SERVER_ADDRESS + testCount)); + localServer.init(); + + localClient = new LocalBulletinBoardClient( + localServer, + THREAD_NUM, + SUBSCRIPTION_INTERVAL); + + synchronizer = new SimpleBulletinBoardSynchronizer(SYNC_SLEEP_INTERVAL, SYNC_WAIT_CAP); + synchronizer.init(localClient, remoteClient); + + semaphore = new Semaphore(0); + thrown = new LinkedList<>(); + + testCount++; + + } + + private class SyncStatusCallback implements FutureCallback { + + private final SyncStatus statusToWaitFor; + private AtomicBoolean stillWaiting; + + public SyncStatusCallback(SyncStatus statusToWaitFor) { + this.statusToWaitFor = statusToWaitFor; + stillWaiting = new AtomicBoolean(true); + } + + @Override + public void onSuccess(SyncStatus result) { + + if (result == statusToWaitFor && stillWaiting.compareAndSet(true, false)){ + semaphore.release(); + } + + } + + @Override + public void onFailure(Throwable t) { + thrown.add(t); + if (stillWaiting.compareAndSet(true,false)) { + semaphore.release(); + } + } + } + + private class MessageCountCallback implements FutureCallback { + + private int[] expectedCounts; + private int currentIteration; + + public MessageCountCallback(int[] expectedCounts) { + this.expectedCounts = expectedCounts; + this.currentIteration = 0; + } + + @Override + public void onSuccess(Integer result) { + + if (currentIteration < expectedCounts.length){ + if (result != expectedCounts[currentIteration]){ + onFailure(new AssertionError("Wrong message count. Expected " + expectedCounts[currentIteration] + " but received " + result)); + currentIteration = expectedCounts.length; + return; + } + } + + currentIteration++; + + if (currentIteration == expectedCounts.length) + semaphore.release(); + } + + @Override + public void onFailure(Throwable t) { + thrown.add(t); + semaphore.release(); + } + } + + @Test + public void testSync() throws SignatureException, CommunicationException, InterruptedException { + + Timestamp timestamp = Timestamp.newBuilder() + .setSeconds(15252162) + .setNanos(85914) + .build(); + + BulletinBoardMessage msg = messageGenerator.generateRandomMessage(signers, timestamp, 10, 10); + + MessageID msgID = localClient.postMessage(msg); + + timestamp = Timestamp.newBuilder() + .setSeconds(51511653) + .setNanos(3625) + .build(); + + 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)); + + int[] expectedCounts = {2,0}; + synchronizer.subscribeToRemainingMessagesCount(new MessageCountCallback(expectedCounts)); + + Thread syncThread = new Thread(synchronizer); + syncThread.start(); + + if (!semaphore.tryAcquire(2, 4000, TimeUnit.MILLISECONDS)) { + thrown.add(new TimeoutException("Timeout occurred while waiting for synchronizer to sync.")); + } + + synchronizer.stop(); + syncThread.join(); + + if (thrown.size() > 0) { + for (Throwable t : thrown) + System.err.println(t.getMessage()); + assertThat("Exception thrown by Synchronizer: " + thrown.get(0).getMessage(), false); + } + + List msgList = remoteClient.readMessages(MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .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); + + BulletinBoardMessage returnedBatchMsg = remoteClient.readMessage(batchMsgID); + + assertThat("Returned batch does not equal original one.", messageComparator.compare(returnedBatchMsg, batchMessage) == 0); + + } + + @Test + public void testServerError() throws SignatureException, CommunicationException, InterruptedException { + + Timestamp timestamp = Timestamp.newBuilder() + .setSeconds(945736256) + .setNanos(276788) + .build(); + + BulletinBoardMessage msg = messageGenerator.generateRandomMessage(signers, timestamp, 10, 10); + + remoteClient.close(); + + synchronizer.subscribeToSyncStatus(new SyncStatusCallback(SyncStatus.SERVER_ERROR)); + + localClient.postMessage(msg); + + Thread thread = new Thread(synchronizer); + + thread.start(); + + if (!semaphore.tryAcquire(4000, TimeUnit.MILLISECONDS)) { + thrown.add(new TimeoutException("Timeout occurred while waiting for synchronizer to sync.")); + } + + synchronizer.stop(); + thread.join(); + + } + + @After + public void close() { + + if (thrown.size() > 0) { + for (Throwable t : thrown) { + System.err.println(t.getMessage()); + } + assertThat("Exception thrown by Synchronizer: " + thrown.get(0).getMessage(), false); + } + + synchronizer.stop(); + localClient.close(); + remoteClient.close(); + + } + +} diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/CachedBulletinBoardClientTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/CachedBulletinBoardClientTest.java new file mode 100644 index 0000000..8bae8c2 --- /dev/null +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/CachedBulletinBoardClientTest.java @@ -0,0 +1,111 @@ +package meerkat.bulletinboard; + +import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; +import meerkat.bulletinboard.sqlserver.H2QueryProvider; +import meerkat.comm.CommunicationException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.security.SignatureException; +import java.util.LinkedList; +import java.util.List; + +/** + * Created by Arbel on 6/27/2016. + */ +public class CachedBulletinBoardClientTest { + + private static final int THREAD_NUM = 3; + + private static final String LOCAL_DB_NAME = "localDB"; + private static final String REMOTE_DB_NAME = "remoteDB"; + private static final String QUEUE_DB_NAME = "queueDB"; + + private static final int SUBSRCIPTION_DELAY = 500; + private static final int SYNC_DELAY = 500; + + // Testers + private CachedBulletinBoardClient cachedClient; + private GenericBulletinBoardClientTester clientTest; + private GenericSubscriptionClientTester subscriptionTester; + + public CachedBulletinBoardClientTest() throws CommunicationException { + + DeletableBulletinBoardServer localServer = new BulletinBoardSQLServer(new H2QueryProvider(LOCAL_DB_NAME)); + localServer.init(); + LocalBulletinBoardClient localClient = new LocalBulletinBoardClient(localServer, THREAD_NUM, SUBSRCIPTION_DELAY); + + DeletableBulletinBoardServer remoteServer = new BulletinBoardSQLServer(new H2QueryProvider(REMOTE_DB_NAME)); + remoteServer.init(); + LocalBulletinBoardClient remoteClient = new LocalBulletinBoardClient(remoteServer, THREAD_NUM, SUBSRCIPTION_DELAY); + + DeletableBulletinBoardServer queueServer = new BulletinBoardSQLServer(new H2QueryProvider(QUEUE_DB_NAME)); + queueServer.init(); + LocalBulletinBoardClient queueClient = new LocalBulletinBoardClient(queueServer, THREAD_NUM, SUBSRCIPTION_DELAY); + + List clientList = new LinkedList<>(); + clientList.add(remoteClient); + + BulletinBoardSubscriber subscriber = new ThreadedBulletinBoardSubscriber(clientList, localClient); + + cachedClient = new CachedBulletinBoardClient(localClient, remoteClient, subscriber, queueClient, SYNC_DELAY, SYNC_DELAY); + subscriptionTester = new GenericSubscriptionClientTester(cachedClient); + clientTest = new GenericBulletinBoardClientTester(cachedClient, 87351); + + } + + // Test methods + + /** + * Takes care of initializing the client and the test resources + */ + @Before + public void init(){ + + clientTest.init(); + + } + + /** + * Closes the client and makes sure the test fails when an exception occurred in a separate thread + */ + + @After + public void close() { + + cachedClient.close(); + clientTest.close(); + + } + + @Test + public void testPost() { + + clientTest.testPost(); + + } + + @Test + public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException { + + clientTest.testBatchPost(); + } + + @Test + public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException { + + clientTest.testCompleteBatchPost(); + + } + + @Test + public void testSubscription() throws SignatureException, CommunicationException { + +// subscriptionTester.init(); +// subscriptionTester.subscriptionTest(); +// subscriptionTester.close(); + + } + +} diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericBulletinBoardClientTester.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericBulletinBoardClientTester.java index d4bd3ec..7ded2e4 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericBulletinBoardClientTester.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericBulletinBoardClientTester.java @@ -5,10 +5,13 @@ import com.google.protobuf.ByteString; import com.google.protobuf.Timestamp; import meerkat.comm.CommunicationException; import meerkat.crypto.concrete.ECDSASignature; +import meerkat.crypto.concrete.SHA256Digest; import meerkat.protobuf.BulletinBoardAPI.*; import meerkat.protobuf.Crypto; import meerkat.util.BulletinBoardMessageComparator; import meerkat.util.BulletinBoardMessageGenerator; +import meerkat.util.BulletinBoardUtils; +import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; import java.io.IOException; import java.io.InputStream; @@ -28,7 +31,7 @@ public class GenericBulletinBoardClientTester { // Signature resources - private GenericBatchDigitalSignature signers[]; + private BulletinBoardSignature signers[]; private ByteString[] signerIDs; private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12"; @@ -45,28 +48,29 @@ public class GenericBulletinBoardClientTester { private AsyncBulletinBoardClient bulletinBoardClient; private PostCallback postCallback; - private PostCallback failPostCallback = new PostCallback(true,false); private RedundancyCallback redundancyCallback; private ReadCallback readCallback; - private ReadBatchCallback readBatchCallback; // Sync and misc private Semaphore jobSemaphore; private Vector thrown; private Random random; + private BulletinBoardMessageGenerator generator; + + private BulletinBoardDigest digest; // Constructor - public GenericBulletinBoardClientTester(AsyncBulletinBoardClient bulletinBoardClient){ + public GenericBulletinBoardClientTester(AsyncBulletinBoardClient bulletinBoardClient, int seed){ this.bulletinBoardClient = bulletinBoardClient; - signers = new GenericBatchDigitalSignature[2]; + signers = new GenericBulletinBoardSignature[2]; signerIDs = new ByteString[signers.length]; - signers[0] = new GenericBatchDigitalSignature(new ECDSASignature()); - signers[1] = new GenericBatchDigitalSignature(new ECDSASignature()); + signers[0] = new GenericBulletinBoardSignature(new ECDSASignature()); + signers[1] = new GenericBulletinBoardSignature(new ECDSASignature()); InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE); char[] password = KEYFILE_PASSWORD1.toCharArray(); @@ -108,6 +112,10 @@ public class GenericBulletinBoardClientTester { fail("Couldn't find signing key " + e.getMessage()); } + this.random = new Random(seed); + this.generator = new BulletinBoardMessageGenerator(random); + this.digest = new GenericBulletinBoardDigest(new SHA256Digest()); + } // Callback definitions @@ -138,16 +146,21 @@ public class GenericBulletinBoardClientTester { @Override public void onSuccess(Boolean msg) { + System.err.println("Post operation completed"); - jobSemaphore.release(); - //TODO: Change Assert mechanism to exception one + if (isAssert) { - if (assertValue) { - assertThat("Post operation failed", msg, is(Boolean.TRUE)); + if (assertValue && !msg) { + genericHandleFailure(new AssertionError("Post operation failed")); + } else if (!assertValue && msg){ + genericHandleFailure(new AssertionError("Post operation succeeded unexpectedly")); } else { - assertThat("Post operation succeeded unexpectedly", msg, is(Boolean.FALSE)); + jobSemaphore.release(); } + } else { + jobSemaphore.release(); } + } @Override @@ -210,21 +223,24 @@ public class GenericBulletinBoardClientTester { } } - private class ReadBatchCallback implements FutureCallback { + private class ReadBatchCallback implements FutureCallback{ - private CompleteBatch expectedBatch; + private BulletinBoardMessage expectedMsg; - public ReadBatchCallback(CompleteBatch expectedBatch) { - this.expectedBatch = expectedBatch; + public ReadBatchCallback(BulletinBoardMessage expectedMsg) { + this.expectedMsg = expectedMsg; } @Override - public void onSuccess(CompleteBatch batch) { + public void onSuccess(BulletinBoardMessage msg) { - System.err.println(batch); - jobSemaphore.release(); + BulletinBoardMessageComparator msgComparator = new BulletinBoardMessageComparator(); - assertThat("Batch returned is incorrect", batch, is(equalTo(expectedBatch))); + if (msgComparator.compare(msg, expectedMsg) != 0) { + genericHandleFailure(new AssertionError("Batch read returned different message.\nExpected:" + expectedMsg + "\nRecieved:" + msg + "\n")); + } else { + jobSemaphore.release(); + } } @@ -234,59 +250,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++){ - - BatchData batchData = BatchData.newBuilder() - .setData(ByteString.copyFrom(randomByteArray(i))) - .build(); - - completeBatch.appendBatchData(batchData); - - } - - completeBatch.setTimestamp(Timestamp.newBuilder() - .setSeconds(Math.abs(90)) - .setNanos(50) - .build()); - - signers[signer].updateContent(completeBatch); - - completeBatch.setSignature(signers[signer].sign()); - - return completeBatch; - - } - // Test methods /** @@ -311,7 +274,13 @@ public class GenericBulletinBoardClientTester { public void close() { if (thrown.size() > 0) { + + for (Throwable t : thrown){ + System.err.println(t.getMessage()); + } + assert false; + } } @@ -319,7 +288,7 @@ public class GenericBulletinBoardClientTester { /** * Tests the standard post, redundancy and read methods */ - public void postTest() { + public void testPost() { byte[] b1 = {(byte) 1, (byte) 2, (byte) 3, (byte) 4}; byte[] b2 = {(byte) 11, (byte) 12, (byte) 13, (byte) 14}; @@ -395,59 +364,69 @@ public class GenericBulletinBoardClientTester { /** * Tests posting a batch by parts - * Also tests not being able to post to a closed batch * @throws CommunicationException, SignatureException, InterruptedException */ public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException { - final int SIGNER = 1; - final int BATCH_ID = 100; final int BATCH_LENGTH = 100; + final int CHUNK_SIZE = 10; + final int TAG_NUM = 10; - CompleteBatch completeBatch = createRandomBatch(SIGNER, BATCH_ID, BATCH_LENGTH); + final Timestamp timestamp = Timestamp.newBuilder() + .setSeconds(141515) + .setNanos(859018) + .build(); + + final BulletinBoardMessage msg = generator.generateRandomMessage(signers, timestamp, BATCH_LENGTH, TAG_NUM); // Begin batch - bulletinBoardClient.beginBatch(completeBatch.getBeginBatchMessage(), postCallback); + bulletinBoardClient.beginBatch(msg.getMsg().getTagList(), new FutureCallback() { + + @Override + public void onSuccess(final BatchIdentifier identifier) { + + bulletinBoardClient.postBatchData(identifier, BulletinBoardUtils.breakToBatch(msg, CHUNK_SIZE), new FutureCallback() { + + @Override + public void onSuccess(Boolean result) { + + bulletinBoardClient.closeBatch(identifier, msg.getMsg().getTimestamp(), msg.getSigList(), new FutureCallback() { + @Override + public void onSuccess(Boolean result) { + jobSemaphore.release(); + } + + @Override + public void onFailure(Throwable t) { + genericHandleFailure(t); + } + }); + + } + + @Override + public void onFailure(Throwable t) { + genericHandleFailure(t); + } + + }); + + } + + @Override + public void onFailure(Throwable t) { + genericHandleFailure(t); + } + + }); jobSemaphore.acquire(); - // Post data + digest.reset(); + digest.update(msg); - bulletinBoardClient.postBatchData(signerIDs[SIGNER], BATCH_ID, completeBatch.getBatchDataList(), postCallback); - - jobSemaphore.acquire(); - - // Close batch - - CloseBatchMessage closeBatchMessage = completeBatch.getCloseBatchMessage(); - - bulletinBoardClient.closeBatch(closeBatchMessage, postCallback); - - jobSemaphore.acquire(); - - // Attempt to open batch again - - bulletinBoardClient.beginBatch(completeBatch.getBeginBatchMessage(), failPostCallback); - - // Attempt to add batch data - - bulletinBoardClient.postBatchData(signerIDs[SIGNER], BATCH_ID, completeBatch.getBatchDataList(), failPostCallback); - - jobSemaphore.acquire(2); - - // Read batch data - - BatchSpecificationMessage batchSpecificationMessage = - BatchSpecificationMessage.newBuilder() - .setSignerId(signerIDs[SIGNER]) - .setBatchId(BATCH_ID) - .setStartPosition(0) - .build(); - - readBatchCallback = new ReadBatchCallback(completeBatch); - - bulletinBoardClient.readBatch(batchSpecificationMessage, readBatchCallback); + bulletinBoardClient.readMessage(digest.digestAsMessageID(), new ReadBatchCallback(msg)); jobSemaphore.acquire(); @@ -455,62 +434,61 @@ public class GenericBulletinBoardClientTester { /** * Posts a complete batch message - * Checks reading of the message + * Checks reading of the message in two parts * @throws CommunicationException, SignatureException, InterruptedException */ public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException { - final int SIGNER = 0; - final int BATCH_ID = 101; - final int BATCH_LENGTH = 50; + final int BATCH_LENGTH = 100; + final int CHUNK_SIZE = 99; + final int TAG_NUM = 8; + + final Timestamp timestamp = Timestamp.newBuilder() + .setSeconds(7776151) + .setNanos(252616) + .build(); + + final BulletinBoardMessage msg = generator.generateRandomMessage(signers, timestamp, BATCH_LENGTH, TAG_NUM); // Post batch - CompleteBatch completeBatch = createRandomBatch(SIGNER, BATCH_ID, BATCH_LENGTH); - - bulletinBoardClient.postBatch(completeBatch,postCallback); + MessageID msgID = bulletinBoardClient.postAsBatch(msg, CHUNK_SIZE, postCallback); jobSemaphore.acquire(); // Read batch - BatchSpecificationMessage batchSpecificationMessage = - BatchSpecificationMessage.newBuilder() - .setSignerId(signerIDs[SIGNER]) - .setBatchId(BATCH_ID) - .setStartPosition(0) - .build(); + MessageFilterList filterList = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.MSG_ID) + .setId(msgID.getID()) + .build()) + .build(); - readBatchCallback = new ReadBatchCallback(completeBatch); + bulletinBoardClient.readMessages(filterList, new FutureCallback>() { - bulletinBoardClient.readBatch(batchSpecificationMessage, readBatchCallback); + @Override + public void onSuccess(List msgList) { - jobSemaphore.acquire(); + if (msgList.size() != 1) { - } + genericHandleFailure(new AssertionError("Wrong number of stubs returned. Expected: 1; Found: " + msgList.size())); - /** - * Tests that an unopened batch cannot be closed - * @throws CommunicationException, InterruptedException - */ - public void testInvalidBatchClose() throws CommunicationException, InterruptedException { + } else { - final int NON_EXISTENT_BATCH_ID = 999; + BulletinBoardMessage retrievedMsg = msgList.get(0); + bulletinBoardClient.readBatchData(retrievedMsg, new ReadBatchCallback(msg)); - CloseBatchMessage closeBatchMessage = - CloseBatchMessage.newBuilder() - .setBatchId(NON_EXISTENT_BATCH_ID) - .setBatchLength(1) - .setSig(Crypto.Signature.getDefaultInstance()) - .setTimestamp(Timestamp.newBuilder() - .setSeconds(9) - .setNanos(12) - .build()) - .build(); + } - // Try to stop the (unopened) batch; + } - bulletinBoardClient.closeBatch(closeBatchMessage, failPostCallback); + @Override + public void onFailure(Throwable t) { + genericHandleFailure(t); + } + + }); jobSemaphore.acquire(); diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java index a91f8d6..6d02914 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/GenericSubscriptionClientTester.java @@ -16,9 +16,6 @@ import java.security.cert.CertificateException; import java.util.*; import java.util.concurrent.Semaphore; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.startsWith; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** @@ -26,7 +23,7 @@ import static org.junit.Assert.fail; */ public class GenericSubscriptionClientTester { - private GenericBatchDigitalSignature signers[]; + private BulletinBoardSignature signers[]; private ByteString[] signerIDs; private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12"; @@ -38,7 +35,7 @@ public class GenericSubscriptionClientTester { private static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt"; private static String CERT3_PEM_EXAMPLE = "/certs/enduser-certs/user3.crt"; - private SubscriptionAsyncBulletinBoardClient bulletinBoardClient; + private SubscriptionBulletinBoardClient bulletinBoardClient; private Random random; private BulletinBoardMessageGenerator generator; @@ -46,14 +43,14 @@ public class GenericSubscriptionClientTester { private Semaphore jobSemaphore; private Vector thrown; - public GenericSubscriptionClientTester(SubscriptionAsyncBulletinBoardClient bulletinBoardClient){ + public GenericSubscriptionClientTester(SubscriptionBulletinBoardClient bulletinBoardClient){ this.bulletinBoardClient = bulletinBoardClient; - signers = new GenericBatchDigitalSignature[2]; + signers = new BulletinBoardSignature[2]; signerIDs = new ByteString[signers.length]; - signers[0] = new GenericBatchDigitalSignature(new ECDSASignature()); - signers[1] = new GenericBatchDigitalSignature(new ECDSASignature()); + signers[0] = new GenericBulletinBoardSignature(new ECDSASignature()); + signers[1] = new GenericBulletinBoardSignature(new ECDSASignature()); InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE); char[] password = KEYFILE_PASSWORD1.toCharArray(); @@ -181,17 +178,15 @@ public class GenericSubscriptionClientTester { public void onFailure(Throwable t) { System.err.println(t.getCause() + " " + t.getMessage()); thrown.add(t); - jobSemaphore.release(expectedMessages.size()); + jobSemaphore.release(); stage = expectedMessages.size(); } } public void subscriptionTest() throws SignatureException, CommunicationException { - final int FIRST_POST_ID = 201; - final int SECOND_POST_ID = 202; final String COMMON_TAG = "SUBSCRIPTION_TEST"; - + List tags = new LinkedList<>(); tags.add(COMMON_TAG); @@ -207,9 +202,9 @@ public class GenericSubscriptionClientTester { .build(); List> expectedMessages = new ArrayList<>(3); - expectedMessages.add(new LinkedList()); - expectedMessages.add(new LinkedList()); - expectedMessages.add(new LinkedList()); + expectedMessages.add(new LinkedList<>()); + expectedMessages.add(new LinkedList<>()); + expectedMessages.add(new LinkedList<>()); expectedMessages.get(0).add(msg1); expectedMessages.get(2).add(msg3); diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java index d2039ae..d33d5ba 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/LocalBulletinBoardClientTest.java @@ -7,12 +7,6 @@ import org.junit.Before; import org.junit.Test; import java.security.SignatureException; -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.List; - -import static org.junit.Assert.fail; /** * Created by Arbel Deutsch Peled on 05-Dec-15. @@ -30,30 +24,14 @@ public class LocalBulletinBoardClientTest { public LocalBulletinBoardClientTest() throws CommunicationException { - H2QueryProvider queryProvider = new H2QueryProvider(DB_NAME) ; + H2QueryProvider queryProvider = new H2QueryProvider(DB_NAME); - try { - - Connection conn = queryProvider.getDataSource().getConnection(); - Statement stmt = conn.createStatement(); - - List deletionQueries = queryProvider.getSchemaDeletionCommands(); - - for (String deletionQuery : deletionQueries) { - stmt.execute(deletionQuery); - } - - } catch (SQLException e) { - System.err.println(e.getMessage()); - throw new CommunicationException(e.getCause() + " " + e.getMessage()); - } - - BulletinBoardServer server = new BulletinBoardSQLServer(queryProvider); - server.init(DB_NAME); + DeletableBulletinBoardServer server = new BulletinBoardSQLServer(queryProvider); + server.init(); LocalBulletinBoardClient client = new LocalBulletinBoardClient(server, THREAD_NUM, SUBSRCIPTION_DELAY); subscriptionTester = new GenericSubscriptionClientTester(client); - clientTest = new GenericBulletinBoardClientTester(client); + clientTest = new GenericBulletinBoardClientTester(client, 98354); } @@ -81,9 +59,9 @@ public class LocalBulletinBoardClientTest { } @Test - public void postTest() { + public void testPost() { - clientTest.postTest(); + clientTest.testPost(); } @@ -100,15 +78,9 @@ public class LocalBulletinBoardClientTest { } - @Test - public void testInvalidBatchClose() throws CommunicationException, InterruptedException { - - clientTest.testInvalidBatchClose(); - - } - @Test public void testSubscription() throws SignatureException, CommunicationException { + subscriptionTester.init(); subscriptionTester.subscriptionTest(); subscriptionTester.close(); diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/SingleServerBulletinBoardClientIntegrationTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/SingleServerBulletinBoardClientIntegrationTest.java new file mode 100644 index 0000000..7c8d58b --- /dev/null +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/SingleServerBulletinBoardClientIntegrationTest.java @@ -0,0 +1,104 @@ +package meerkat.bulletinboard; + +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.Voting.BulletinBoardClientParams; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.security.SignatureException; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Executors; + +/** + * Created by Arbel Deutsch Peled on 05-Dec-15. + */ +public class SingleServerBulletinBoardClientIntegrationTest { + + // Server data + + private static final String PROP_GETTY_URL = "gretty.httpBaseURI"; + private static final String DEFAULT_BASE_URL = "http://localhost:8081"; + private static final String BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL); + + private static final int THREAD_NUM = 3; + private static final long FAIL_DELAY = 3000; + private static final long SUBSCRIPTION_INTERVAL = 500; + + // Testers + private GenericBulletinBoardClientTester clientTest; + private GenericSubscriptionClientTester subscriptionTester; + + public SingleServerBulletinBoardClientIntegrationTest(){ + + SingleServerBulletinBoardClient client = new SingleServerBulletinBoardClient(THREAD_NUM, FAIL_DELAY, SUBSCRIPTION_INTERVAL); + + List testDB = new LinkedList<>(); + testDB.add(BASE_URL); + + client.init(BulletinBoardClientParams.newBuilder() + .addAllBulletinBoardAddress(testDB) + .setMinRedundancy((float) 1.0) + .build()); + + clientTest = new GenericBulletinBoardClientTester(client, 981541); + subscriptionTester = new GenericSubscriptionClientTester(client); + + } + + // Test methods + + /** + * Takes care of initializing the client and the test resources + */ + @Before + public void init(){ + + clientTest.init(); + + } + + /** + * Closes the client and makes sure the test fails when an exception occurred in a separate thread + */ + + @After + public void close() { + + clientTest.close(); + + } + + @Test + public void testPost() { + + clientTest.testPost(); + + } + + @Test + public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException { + + clientTest.testBatchPost(); + } + + @Test + public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException { + + clientTest.testCompleteBatchPost(); + + } + + @Test + public void testSubscription() throws SignatureException, CommunicationException { + + subscriptionTester.init(); + subscriptionTester.subscriptionTest(); + subscriptionTester.close(); + + } + +} diff --git a/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java b/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java index b69cf72..1187245 100644 --- a/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java +++ b/bulletin-board-client/src/test/java/meerkat/bulletinboard/ThreadedBulletinBoardClientIntegrationTest.java @@ -28,7 +28,7 @@ public class ThreadedBulletinBoardClientIntegrationTest { public ThreadedBulletinBoardClientIntegrationTest(){ - ThreadedBulletinBoardClient client = new ThreadedBulletinBoardClient(); + ThreadedBulletinBoardClient client = new ThreadedBulletinBoardClient(3,0,500); List testDB = new LinkedList<>(); testDB.add(BASE_URL); @@ -38,7 +38,7 @@ public class ThreadedBulletinBoardClientIntegrationTest { .setMinRedundancy((float) 1.0) .build()); - clientTest = new GenericBulletinBoardClientTester(client); + clientTest = new GenericBulletinBoardClientTester(client, 52351); } @@ -66,9 +66,9 @@ public class ThreadedBulletinBoardClientIntegrationTest { } @Test - public void postTest() { + public void testPost() { - clientTest.postTest(); + clientTest.testPost(); } @@ -76,6 +76,7 @@ public class ThreadedBulletinBoardClientIntegrationTest { public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException { clientTest.testBatchPost(); + } @Test @@ -85,11 +86,4 @@ public class ThreadedBulletinBoardClientIntegrationTest { } - @Test - public void testInvalidBatchClose() throws CommunicationException, InterruptedException { - - clientTest.testInvalidBatchClose(); - - } - } diff --git a/bulletin-board-server/.gitignore b/bulletin-board-server/.gitignore index 3e2fcc7..ae3c172 100644 --- a/bulletin-board-server/.gitignore +++ b/bulletin-board-server/.gitignore @@ -1 +1 @@ -/bin/ +/bin/ diff --git a/bulletin-board-server/build.gradle b/bulletin-board-server/build.gradle index 766ed56..1606ef8 100644 --- a/bulletin-board-server/build.gradle +++ b/bulletin-board-server/build.gradle @@ -259,4 +259,3 @@ publishing { } } - diff --git a/bulletin-board-server/gradle-app.setting b/bulletin-board-server/gradle-app.setting index 21226bc..b0bad25 100644 --- a/bulletin-board-server/gradle-app.setting +++ b/bulletin-board-server/gradle-app.setting @@ -1,29 +1,29 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/service/HelloProtoBuf.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/service/HelloProtoBuf.java index c1f5de8..772e7dd 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/service/HelloProtoBuf.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/service/HelloProtoBuf.java @@ -1,30 +1,30 @@ -package meerkat.bulletinboard.service; - -import com.google.protobuf.ByteString; -import com.google.protobuf.Message; -import meerkat.protobuf.Crypto; -import meerkat.protobuf.BulletinBoardAPI.*; - -import java.util.Arrays; -import java.util.List; - -/** - * Created by talm on 10/11/15. - */ -public class HelloProtoBuf { - public Message sayHello() { - BulletinBoardMessage.Builder msg = BulletinBoardMessage.newBuilder(); - - UnsignedBulletinBoardMessage.Builder unsigned = UnsignedBulletinBoardMessage.newBuilder(); - unsigned.setData(ByteString.copyFromUtf8("Hello World!")); - List tags = Arrays.asList("Greetings", "FirstPrograms"); - unsigned.addAllTag(tags); - msg.setMsg(unsigned); - - Crypto.Signature.Builder sig = Crypto.Signature.newBuilder(); - sig.setData(ByteString.copyFromUtf8("deadbeef")); - msg.addSig(sig); - - return msg.build(); - } -} +package meerkat.bulletinboard.service; + +import com.google.protobuf.ByteString; +import com.google.protobuf.Message; +import meerkat.protobuf.Crypto; +import meerkat.protobuf.BulletinBoardAPI.*; + +import java.util.Arrays; +import java.util.List; + +/** + * Created by talm on 10/11/15. + */ +public class HelloProtoBuf { + public Message sayHello() { + BulletinBoardMessage.Builder msg = BulletinBoardMessage.newBuilder(); + + UnsignedBulletinBoardMessage.Builder unsigned = UnsignedBulletinBoardMessage.newBuilder(); + unsigned.setData(ByteString.copyFromUtf8("Hello World!")); + List tags = Arrays.asList("Greetings", "FirstPrograms"); + unsigned.addAllTag(tags); + msg.setMsg(unsigned); + + Crypto.Signature.Builder sig = Crypto.Signature.newBuilder(); + sig.setData(ByteString.copyFromUtf8("deadbeef")); + msg.addSig(sig); + + return msg.build(); + } +} diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java index 02b50c9..bf99259 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java @@ -1,1044 +1,1132 @@ -package meerkat.bulletinboard.sqlserver; - -import java.sql.*; -import java.util.*; - -import com.google.protobuf.*; - -import com.google.protobuf.Timestamp; -import meerkat.bulletinboard.*; -import meerkat.bulletinboard.sqlserver.mappers.*; -import static meerkat.bulletinboard.BulletinBoardConstants.*; - -import meerkat.comm.CommunicationException; - -import meerkat.comm.MessageOutputStream; -import meerkat.crypto.concrete.ECDSASignature; -import meerkat.crypto.concrete.SHA256Digest; - -import meerkat.protobuf.BulletinBoardAPI.*; -import meerkat.protobuf.Comm.*; -import meerkat.protobuf.Crypto.Signature; -import meerkat.protobuf.Crypto.SignatureVerificationKey; - - -import static meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider.*; - -import javax.sql.DataSource; - -import meerkat.util.BulletinBoardUtils; -import meerkat.util.TimestampComparator; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; - - - -/** - * This is a generic SQL implementation of the BulletinBoardServer API. - */ -public class BulletinBoardSQLServer implements BulletinBoardServer{ - - /** - * This interface provides the required implementation-specific data to enable an access to an actual SQL server. - * It accounts for differences in languages between SQL DB types and for the different addresses needed to access them. - */ - public interface SQLQueryProvider { - - /** - * Allowed query types. - * Note that each query returned has to comply with the parameter names specified ny getParamNames - */ - public static enum QueryType { - - FIND_MSG_ID( - new String[] {"MsgId"}, - new int[] {Types.BLOB} - ), - - FIND_TAG_ID( - new String[] {"Tag"}, - new int[] {Types.VARCHAR} - ), - - INSERT_MSG( - new String[] {"MsgId","TimeStamp","Msg"}, - new int[] {Types.BLOB, Types.TIMESTAMP, Types.BLOB} - ), - - INSERT_NEW_TAG( - new String[] {"Tag"}, - new int[] {Types.VARCHAR} - ), - - CONNECT_TAG( - new String[] {"EntryNum","Tag"}, - new int[] {Types.INTEGER, Types.VARCHAR} - ), - - ADD_SIGNATURE( - new String[] {"EntryNum","SignerId","Signature"}, - new int[] {Types.INTEGER, Types.BLOB, Types.BLOB} - ), - - GET_SIGNATURES( - new String[] {"EntryNum"}, - new int[] {Types.INTEGER} - ), - - GET_MESSAGES( - new String[] {}, - new int[] {} - ), - - COUNT_MESSAGES( - new String[] {}, - new int[] {} - ), - - GET_MESSAGE_STUBS( - new String[] {}, - new int[] {} - ), - - GET_LAST_MESSAGE_ENTRY( - new String[] {}, - new int[] {} - ), - - GET_BATCH_MESSAGE_ENTRY( - new String[] {"SignerId", "BatchId"}, - new int[] {Types.BLOB, Types.INTEGER} - ), - - CHECK_BATCH_LENGTH( - new String[] {"SignerId", "BatchId"}, - new int[] {Types.BLOB, Types.INTEGER} - ), - - GET_BATCH_MESSAGE_DATA( - new String[] {"SignerId", "BatchId", "StartPosition"}, - new int[] {Types.BLOB, Types.INTEGER, Types.INTEGER} - ), - - INSERT_BATCH_DATA( - new String[] {"SignerId", "BatchId", "SerialNum", "Data"}, - new int[] {Types.BLOB, Types.INTEGER, Types.INTEGER, Types.BLOB} - ), - - CONNECT_BATCH_TAG( - new String[] {"SignerId", "BatchId", "Tag"}, - new int[] {Types.BLOB, Types.INTEGER, Types.VARCHAR} - ), - - GET_BATCH_TAGS( - new String[] {"SignerId", "BatchId"}, - new int[] {Types.BLOB, Types.INTEGER} - ), - - REMOVE_BATCH_TAGS( - new String[] {"SignerId", "BatchId"}, - new int[] {Types.BLOB, Types.INTEGER} - ); - - private String[] paramNames; - private int[] paramTypes; - - private QueryType(String[] paramNames, int[] paramTypes) { - this.paramNames = paramNames; - this.paramTypes = paramTypes; - } - - public String[] getParamNames() { - return paramNames; - } - - public String getParamName(int num) { - return paramNames[num]; - } - - public int[] getParamTypes() { - return paramTypes; - } - - public int getParamType(int num) { - return paramTypes[num]; - } - - } - - /** - * This enum provides the standard translation between a filter type and the corresponding parameter name in the SQL query - */ - public static enum FilterTypeParam { - - ENTRY_NUM("EntryNum", Types.INTEGER), - MSG_ID("MsgId", Types.BLOB), - SIGNER_ID("SignerId", Types.BLOB), - TAG("Tag", Types.VARCHAR), - LIMIT("Limit", Types.INTEGER), - TIMESTAMP("TimeStamp", Types.TIMESTAMP); - - private FilterTypeParam(String paramName, int paramType) { - this.paramName = paramName; - this.paramType = paramType; - } - - private String paramName; - private int paramType; - - public static FilterTypeParam getFilterTypeParamName(FilterType filterType) { - switch (filterType) { - - case MSG_ID: - return MSG_ID; - - case EXACT_ENTRY: // Go through - case MAX_ENTRY: // Go through - case MIN_ENTRY: - return ENTRY_NUM; - - case SIGNER_ID: - return SIGNER_ID; - - case TAG: - return TAG; - - case MAX_MESSAGES: - return LIMIT; - - case BEFORE_TIME: // Go through - case AFTER_TIME: - return TIMESTAMP; - - default: - return null; - } - } - - public String getParamName() { - return paramName; - } - - public int getParamType() { - return paramType; - } - } - - /** - * This function translates a QueryType into an actual SQL query. - * @param queryType is the type of query requested - * @return a string representation of the query for the specific type of SQL database implemented. - */ - public String getSQLString(QueryType queryType) throws IllegalArgumentException; - - /** - * Used to retrieve a condition to add to an SQL statement that will make the result comply with the filter type - * @param filterType is the filter type - * @param serialNum is a unique number used to identify the condition variables from other condition instances - * @return The SQL string for the condition - * @throws IllegalArgumentException if the filter type used is not supported - */ - public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException; - - public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException; - - /** - * @return the string needed in order to connect to the DB. - */ - public DataSource getDataSource(); - - /** - * This is used to get a list of queries that together create the schema needed for the DB. - * Note that these queries should not assume anything about the current state of the DB. - * In particular: they should not erase any existing tables and/or entries. - * @return The list of queries. - */ - public List getSchemaCreationCommands(); - - /** - * This is used to get a list of queries that together delete the schema needed for the DB. - * This is useful primarily for tests, in which we want to make sure we start with a clean DB. - * @return The list of queries. - */ - public List getSchemaDeletionCommands(); - - } - - /** - * This method returns the value of the parameter specified in a message filter - * @param messageFilter is the filter - * @return the object parameter for the SQL query embedded in the filter (this depends on the filter type) - */ - private Object getParam(MessageFilter messageFilter) { - - switch (messageFilter.getType()) { - - case MSG_ID: // Go through - case SIGNER_ID: - return messageFilter.getId().toByteArray(); - - case EXACT_ENTRY: // Go through - case MAX_ENTRY: // Go through - case MIN_ENTRY: - return messageFilter.getEntry(); - - case TAG: - return messageFilter.getTag(); - - case MAX_MESSAGES: - return messageFilter.getMaxMessages(); - - case BEFORE_TIME: // Go through - case AFTER_TIME: - return BulletinBoardUtils.toSQLTimestamp(messageFilter.getTimestamp()); - - default: // Unsupported filter type - return null; - } - - } - - /** - * This class implements a comparator for the MessageFilter class - * The comparison is done solely by comparing the type of the filter - * This is used to sort the filters by type - */ - public class FilterTypeComparator implements Comparator { - - @Override - public int compare(MessageFilter filter1, MessageFilter filter2) { - return filter1.getTypeValue() - filter2.getTypeValue(); - } - } - - protected SQLQueryProvider sqlQueryProvider; - - protected NamedParameterJdbcTemplate jdbcTemplate; - - protected BatchDigest digest; - protected BatchDigitalSignature signer; - - protected List trusteeSignatureVerificationArray; - protected int minTrusteeSignatures; - - protected List> pollingCommitteeSignatureVerificationKeyArray; - protected int minCommiteeSignatures; - - /** - * This constructor sets the type of SQL language in use. - * @param sqlQueryProvider is the provider of the SQL query strings required for actual operation of the server. - */ - public BulletinBoardSQLServer(SQLQueryProvider sqlQueryProvider) { - this.sqlQueryProvider = sqlQueryProvider; - } - - /** - * This method creates the schema in the given DB to prepare for future transactions - * It does not assume anything about the current state of the database - * @throws SQLException - */ - private void createSchema() throws SQLException { - - for (String command : sqlQueryProvider.getSchemaCreationCommands()) { - jdbcTemplate.update(command,(Map) null); - } - - } - - /** - * This method initializes the signatures, connects to the DB and creates the schema (if required). - */ - @Override - public void init(String meerkatDB) throws CommunicationException { - // TODO write signature reading part. - - digest = new GenericBatchDigest(new SHA256Digest()); - signer = new GenericBatchDigitalSignature(new ECDSASignature()); - - jdbcTemplate = new NamedParameterJdbcTemplate(sqlQueryProvider.getDataSource()); - - try { - createSchema(); - } catch (SQLException e) { - throw new CommunicationException("Couldn't create schema " + e.getMessage()); - } - - } - - /** - * This method verifies the authenticity of the received message based on - * the stored signatures. - * - * @param msg - * is the message to authenticate (containing the signature). - * @return TRUE if the message is authenticated and FALSE otherwise. - */ - private boolean verifyMessage(BulletinBoardMessage msg) { - //TODO: Replace with actual verification. - return true; - } - - /** - * This procedure makes sure that all tags in the given list have an entry in the tags table. - * @param tags - */ - protected void insertNewTags(String[] tags) throws SQLException { - - String sql; - - sql = sqlQueryProvider.getSQLString(QueryType.INSERT_NEW_TAG); - Map namedParameters[] = new HashMap[tags.length]; - - for (int i = 0 ; i < tags.length ; i++){ - namedParameters[i] = new HashMap(); - namedParameters[i].put(QueryType.INSERT_NEW_TAG.getParamName(0), tags[i]); - } - - jdbcTemplate.batchUpdate(sql, namedParameters); - - } - - /** - * This procedure is used to convert a boolean to a BoolValue. - * @param b is the boolean to convert. - * @return a ProtoBuf message with boolean payload. - */ - private BoolValue boolToBoolValue(boolean b){ - return BoolValue.newBuilder() - .setValue(b) - .build(); - } - - - /** - * This method posts a messages to the server - * @param msg is the message to post - * @param checkSignature decides whether ot not the method should check the signature before it posts the message - * @return TRUE if the post is successful and FALSE otherwise - * @throws CommunicationException - */ - public BoolValue postMessage(BulletinBoardMessage msg, boolean checkSignature) throws CommunicationException{ - - if (checkSignature && !verifyMessage(msg)) { - return boolToBoolValue(false); - } - - String sql; - Map[] namedParameterArray; - - byte[] msgID; - long entryNum; - - ProtocolStringList tagList; - String[] tags; - - List signatureList; - Signature[] signatures; - - // Calculate message ID (depending only on the the unsigned message) - - digest.reset(); - digest.update(msg.getMsg()); - - msgID = digest.digest(); - - // Add message to table if needed and store entry number of message. - - sql = sqlQueryProvider.getSQLString(QueryType.FIND_MSG_ID); - Map namedParameters = new HashMap(); - - namedParameters.put(QueryType.FIND_MSG_ID.getParamName(0),msgID); - - List entryNums = jdbcTemplate.query(sql, namedParameters, new LongMapper()); - - if (entryNums.size() > 0){ - - entryNum = entryNums.get(0); - - } else{ - - sql = sqlQueryProvider.getSQLString(QueryType.INSERT_MSG); - - namedParameters.put(QueryType.INSERT_MSG.getParamName(1), BulletinBoardUtils.toSQLTimestamp(msg.getMsg().getTimestamp())); - namedParameters.put(QueryType.INSERT_MSG.getParamName(2), msg.getMsg().toByteArray()); - - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(sql, new MapSqlParameterSource(namedParameters), keyHolder); - - entryNum = keyHolder.getKey().longValue(); - - } - - // Retrieve tags and store new ones in tag table. - - try { - - tagList = msg.getMsg().getTagList(); - tags = new String[tagList.size()]; - tags = tagList.toArray(tags); - - insertNewTags(tags); - - } catch (SQLException e) { - throw new CommunicationException(e.getMessage()); - } - - // Connect message to tags. - - sql = sqlQueryProvider.getSQLString(QueryType.CONNECT_TAG); - - namedParameterArray = new HashMap[tags.length]; - - for (int i = 0 ; i < tags.length ; i++) { - namedParameterArray[i] = new HashMap(); - namedParameterArray[i].put(QueryType.CONNECT_TAG.getParamName(0), entryNum); - namedParameterArray[i].put(QueryType.CONNECT_TAG.getParamName(1), tags[i]); - } - - jdbcTemplate.batchUpdate(sql, namedParameterArray); - - // Retrieve signatures. - - signatureList = msg.getSigList(); - signatures = new Signature[signatureList.size()]; - signatures = signatureList.toArray(signatures); - - // Connect message to signatures. - - sql = sqlQueryProvider.getSQLString(QueryType.ADD_SIGNATURE); - - namedParameterArray = new HashMap[signatures.length]; - - for (int i = 0 ; i < signatures.length ; i++) { - namedParameterArray[i] = new HashMap(); - namedParameterArray[i].put(QueryType.ADD_SIGNATURE.getParamName(0), entryNum); - namedParameterArray[i].put(QueryType.ADD_SIGNATURE.getParamName(1), signatures[i].getSignerId().toByteArray()); - namedParameterArray[i].put(QueryType.ADD_SIGNATURE.getParamName(2), signatures[i].toByteArray()); - } - - jdbcTemplate.batchUpdate(sql,namedParameterArray); - - return boolToBoolValue(true); - - } - - @Override - public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException { - return postMessage(msg, true); // Perform a post and check the signature for authenticity - } - - - /** - * This is a container class for and SQL string builder and a MapSqlParameterSource to be used with it - */ - class SQLAndParameters { - - public StringBuilder sql; - public MapSqlParameterSource parameters; - - public SQLAndParameters(int numOfFilters) { - sql = new StringBuilder(50 * numOfFilters); - parameters = new MapSqlParameterSource(); - } - - } - - SQLAndParameters getSQLFromFilters(MessageFilterList filterList) { - - SQLAndParameters result = new SQLAndParameters(filterList.getFilterCount()); - - List filters = new ArrayList(filterList.getFilterList()); - - Collections.sort(filters, new FilterTypeComparator()); - - boolean isFirstFilter = true; - - if (!filters.isEmpty()) { - result.sql.append(" WHERE "); - - for (int paramNum = 0 ; paramNum < filters.size() ; paramNum++) { - - MessageFilter filter = filters.get(paramNum); - - if (filter.getType().getNumber() != FilterType.MAX_MESSAGES_VALUE) { - if (isFirstFilter) { - isFirstFilter = false; - } else { - result.sql.append(" AND "); - } - } - - result.sql.append(sqlQueryProvider.getCondition(filter.getType(), paramNum)); - - FilterTypeParam filterTypeParam = FilterTypeParam.getFilterTypeParamName(filter.getType()); - - result.parameters.addValue( - filterTypeParam.getParamName() + Integer.toString(paramNum), - getParam(filter), - filterTypeParam.getParamType(), - sqlQueryProvider.getConditionParamTypeName(filter.getType())); - - } - - } - - return result; - - } - - /** - * Private implementation of the message stub reader for returning result as a list - * @param filterList is a filter list that defines which messages the client is interested in - * @return the requested list of message stubs - */ - private List readMessageStubs(MessageFilterList filterList) { - - StringBuilder sqlBuilder = new StringBuilder(50 * (filterList.getFilterCount() + 1)); - - sqlBuilder.append(sqlQueryProvider.getSQLString(QueryType.GET_MESSAGE_STUBS)); - - // Get Conditions - - SQLAndParameters sqlAndParameters = getSQLFromFilters(filterList); - - sqlBuilder.append(sqlAndParameters.sql); - - // Run query - - return jdbcTemplate.query(sqlBuilder.toString(), sqlAndParameters.parameters, new MessageStubMapper()); - - } - - - @Override - public void readMessages(MessageFilterList filterList, MessageOutputStream out) throws CommunicationException { - - BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder(); - - // SQL length is roughly 50 characters per filter + 50 for the query itself - StringBuilder sqlBuilder = new StringBuilder(50 * (filterList.getFilterCount() + 1)); - - // Check if Tag/Signature tables are required for filtering purposes - - sqlBuilder.append(sqlQueryProvider.getSQLString(QueryType.GET_MESSAGES)); - - // Get conditions - - SQLAndParameters sqlAndParameters = getSQLFromFilters(filterList); - sqlBuilder.append(sqlAndParameters.sql); - - // Run query and stream the output using a MessageCallbackHandler - - jdbcTemplate.query(sqlBuilder.toString(), sqlAndParameters.parameters, new MessageCallbackHandler(jdbcTemplate, sqlQueryProvider, out)); - - } - - - /** - * This method returns a string representation of the tag associated with a batch ID - * @param batchId is the given batch ID - * @return the String representation of the tag - */ - private String batchIdToTag(int batchId) { - return BATCH_ID_TAG_PREFIX + Integer.toString(batchId); - } - - - /** - * This method checks if a specified batch exists and is already closed - * @param signerId is the ID of the publisher of the batch - * @param batchId is the unique (per signer) batch ID - * @return TRUE if the batch is closed and FALSE if it is still open or doesn't exist at all - */ - private boolean isBatchClosed(ByteString signerId, int batchId) throws CommunicationException { - - MessageFilterList filterList = MessageFilterList.newBuilder() - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.SIGNER_ID) - .setId(signerId) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(BATCH_TAG) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(batchIdToTag(batchId)) - .build()) - .build(); - - // SQL length is roughly 50 characters per filter + 50 for the query itself - StringBuilder sqlBuilder = new StringBuilder(50 * (filterList.getFilterCount() + 1)); - - // Check if Tag/Signature tables are required for filtering purposes - - sqlBuilder.append(sqlQueryProvider.getSQLString(QueryType.COUNT_MESSAGES)); - - // Get conditions - - SQLAndParameters sqlAndParameters = getSQLFromFilters(filterList); - sqlBuilder.append(sqlAndParameters.sql); - - // Run query and stream the output using a MessageCallbackHandler - - List count = jdbcTemplate.query(sqlBuilder.toString(), sqlAndParameters.parameters, new LongMapper()); - - return (count.size() > 0) && (count.get(0) > 0); - - } - - - @Override - public BoolValue beginBatch(BeginBatchMessage message) throws CommunicationException { - - // Check if batch is closed - if (isBatchClosed(message.getSignerId(), message.getBatchId())) { - return BoolValue.newBuilder().setValue(false).build(); - } - - // Add new tags to table - ProtocolStringList tagList = message.getTagList(); - String[] tags = new String[tagList.size()]; - tags = tagList.toArray(tags); - try { - insertNewTags(tags); - } catch (SQLException e) { - throw new CommunicationException(e.getMessage()); - } - - // Connect tags - String sql = sqlQueryProvider.getSQLString(QueryType.CONNECT_BATCH_TAG); - MapSqlParameterSource namedParameters[] = new MapSqlParameterSource[tags.length]; - - for (int i=0 ; i < tags.length ; i++) { - namedParameters[i] = new MapSqlParameterSource(); - namedParameters[i].addValue(QueryType.CONNECT_BATCH_TAG.getParamName(0),message.getSignerId().toByteArray()); - namedParameters[i].addValue(QueryType.CONNECT_BATCH_TAG.getParamName(1),message.getBatchId()); - namedParameters[i].addValue(QueryType.CONNECT_BATCH_TAG.getParamName(2),tags[i]); - } - - jdbcTemplate.batchUpdate(sql,namedParameters); - - return BoolValue.newBuilder().setValue(true).build(); - - } - - - @Override - public BoolValue postBatchMessage(BatchMessage batchMessage) throws CommunicationException{ - - // Check if batch is closed - if (isBatchClosed(batchMessage.getSignerId(), batchMessage.getBatchId())) { - return BoolValue.newBuilder().setValue(false).build(); - } - - // Add data - String sql = sqlQueryProvider.getSQLString(QueryType.INSERT_BATCH_DATA); - MapSqlParameterSource namedParameters = new MapSqlParameterSource(); - - namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(0),batchMessage.getSignerId().toByteArray()); - namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(1),batchMessage.getBatchId()); - namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(2),batchMessage.getSerialNum()); - namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(3),batchMessage.getData().toByteArray()); - - jdbcTemplate.update(sql, namedParameters); - - return BoolValue.newBuilder().setValue(true).build(); - - } - - - @Override - public BoolValue closeBatchMessage(CloseBatchMessage message) throws CommunicationException { - - ByteString signerId = message.getSig().getSignerId(); - int batchId = message.getBatchId(); - - KeyHolder keyHolder = new GeneratedKeyHolder(); - - // Check batch size - - String sql = sqlQueryProvider.getSQLString(QueryType.CHECK_BATCH_LENGTH); - MapSqlParameterSource namedParameters = new MapSqlParameterSource(); - - - namedParameters.addValue(QueryType.CHECK_BATCH_LENGTH.getParamName(0),signerId.toByteArray()); - namedParameters.addValue(QueryType.CHECK_BATCH_LENGTH.getParamName(1),batchId); - - List lengthResult = jdbcTemplate.query(sql, namedParameters, new LongMapper()); - - if (lengthResult.get(0) != message.getBatchLength()) { - return BoolValue.newBuilder().setValue(false).build(); - } - - // Get Tags and add them to CompleteBatch - - sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_TAGS); - namedParameters = new MapSqlParameterSource(); - - namedParameters.addValue(QueryType.GET_BATCH_TAGS.getParamName(0),signerId.toByteArray()); - namedParameters.addValue(QueryType.GET_BATCH_TAGS.getParamName(1),batchId); - - List tags = jdbcTemplate.query(sql, namedParameters, new StringMapper()); - - CompleteBatch completeBatch = new CompleteBatch( - BeginBatchMessage.newBuilder() - .setSignerId(signerId) - .setBatchId(batchId) - .addAllTag(tags) - .build() - ); - - // Add timestamp to CompleteBatch - completeBatch.setTimestamp(message.getTimestamp()); - - // Add actual batch data to CompleteBatch - - sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA); - namedParameters = new MapSqlParameterSource(); - - namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),signerId.toByteArray()); - namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),batchId); - namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2),0); // Read from the beginning - - completeBatch.appendBatchData(jdbcTemplate.query(sql, namedParameters, new BatchDataMapper())); - - // Verify signature - - completeBatch.setSignature(message.getSig()); - -// try { -// TODO: Actual verification -// //signer.verify(completeBatch); -// } catch (CertificateException | InvalidKeyException | SignatureException e) { -// return BoolValue.newBuilder().setValue(false).build(); -// } - - // Batch verified: finalize it - - // Calculate message ID - digest.reset(); - digest.update(completeBatch); - MessageID msgID = MessageID.newBuilder().setID(ByteString.copyFrom(digest.digest())).build(); - - // Create Bulletin Board message - BulletinBoardMessage bulletinBoardMessage = BulletinBoardMessage.newBuilder() - .addSig(message.getSig()) - .setMsg(UnsignedBulletinBoardMessage.newBuilder() - .addAllTag(tags) - .addTag(BATCH_TAG) - .addTag(batchIdToTag(batchId)) - .setData(message.getSig().getSignerId()) - .setTimestamp(message.getTimestamp()) - .build()) - .build(); - - // Post message without checking signature validity - postMessage(bulletinBoardMessage, false); - - // Remove tags from temporary table - sql = sqlQueryProvider.getSQLString(QueryType.REMOVE_BATCH_TAGS); - namedParameters = new MapSqlParameterSource(); - - namedParameters.addValue(QueryType.REMOVE_BATCH_TAGS.getParamName(0), signerId.toByteArray()); - namedParameters.addValue(QueryType.REMOVE_BATCH_TAGS.getParamName(1), batchId); - - jdbcTemplate.update(sql, namedParameters); - - // Return TRUE - - return BoolValue.newBuilder().setValue(true).build(); - } - - - @Override - public void readBatch(BatchSpecificationMessage message, MessageOutputStream out) throws CommunicationException, IllegalArgumentException{ - - // Check that batch is closed - if (!isBatchClosed(message.getSignerId(), message.getBatchId())) { - throw new IllegalArgumentException("No such batch"); - } - - String sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA); - MapSqlParameterSource namedParameters = new MapSqlParameterSource(); - - namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),message.getSignerId().toByteArray()); - namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),message.getBatchId()); - namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2),message.getStartPosition()); - - jdbcTemplate.query(sql, namedParameters, new BatchDataCallbackHandler(out)); - - } - - - /** - * Finds the entry number of the last entry in the database - * @return the entry number, or -1 if no entries are found - */ - protected long getLastMessageEntry() { - - String sql = sqlQueryProvider.getSQLString(QueryType.GET_LAST_MESSAGE_ENTRY); - - List resultList = jdbcTemplate.query(sql, new LongMapper()); - - if (resultList.size() <= 0){ - return -1; - } - - return resultList.get(0); - - } - - @Override - public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) { - - if (generateSyncQueryParams == null - || !generateSyncQueryParams.hasFilterList() - || generateSyncQueryParams.getFilterList().getFilterCount() <= 0 - || generateSyncQueryParams.getBreakpointListCount() <= 0){ - - return SyncQuery.getDefaultInstance(); - - } - - List messages = readMessageStubs(generateSyncQueryParams.getFilterList()); - - if (messages.size() <= 0){ - return SyncQuery.newBuilder().build(); - } - - SyncQuery.Builder resultBuilder = SyncQuery.newBuilder(); - - Iterator messageIterator = messages.iterator(); - Iterator breakpointIterator = generateSyncQueryParams.getBreakpointListList().iterator(); - - Checksum checksum = new SimpleChecksum(); - checksum.setDigest(new SHA256Digest()); - - Timestamp lastTimestamp = Timestamp.getDefaultInstance(); - BulletinBoardMessage message = messageIterator.next(); - long currentMessageNum = 1; - - boolean checksumChanged = true; - - while (breakpointIterator.hasNext()){ - - Float breakpoint = breakpointIterator.next(); - - // Continue while breakpoint not reached, or it has been reached but no new timestamp has been encountered since - while ( messageIterator.hasNext() - && ((float) currentMessageNum / (float) messages.size() <= breakpoint) - || ((float) currentMessageNum / (float) messages.size() > breakpoint - && lastTimestamp.equals(message.getMsg().getTimestamp()))){ - - checksumChanged = true; - - checksum.update(message.getMsg().getData()); - - lastTimestamp = message.getMsg().getTimestamp(); - message = messageIterator.next(); - - } - - if (checksumChanged) { - - checksum.update(message.getMsg().getData()); - resultBuilder.addQuery(SingleSyncQuery.newBuilder() - .setTimeOfSync(message.getMsg().getTimestamp()) - .setChecksum(checksum.getChecksum()) - .build()); - - } - - checksumChanged = false; - - } - - return resultBuilder.build(); - - } - - - /** - * Searches for the latest time of sync of the DB relative to a given query and returns the metadata needed to complete the sync - * The checksum up to (and including) each given timestamp is calculated using an instance of SimpleChecksum - * @param syncQuery contains a succinct representation of states to compare against - * @return the current last entry num and latest time of sync if there is one; -1 as last entry and empty timestamp otherwise - * @throws CommunicationException - */ - @Override - public SyncQueryResponse querySync(SyncQuery syncQuery) throws CommunicationException { - - if (syncQuery == null){ - return SyncQueryResponse.newBuilder() - .setLastEntryNum(-1) - .setLastTimeOfSync(com.google.protobuf.Timestamp.getDefaultInstance()) - .build(); - } - - com.google.protobuf.Timestamp lastTimeOfSync = null; - - TimestampComparator timestampComparator = new TimestampComparator(); - - long lastEntryNum = getLastMessageEntry(); - - Iterator queryIterator = syncQuery.getQueryList().iterator(); - - SingleSyncQuery currentQuery = queryIterator.next(); - - List messageStubs = readMessageStubs(syncQuery.getFilterList()); - - Checksum checksum = new SimpleChecksum(); - - for (BulletinBoardMessage message : messageStubs){ - - // Check for end of current query - if (timestampComparator.compare(message.getMsg().getTimestamp(), currentQuery.getTimeOfSync()) > 0){ - - if (checksum.getChecksum() == currentQuery.getChecksum()){ - lastTimeOfSync = currentQuery.getTimeOfSync(); - } else { - break; - } - - if (queryIterator.hasNext()){ - currentQuery = queryIterator.next(); - } else{ - break; - } - - } - - // Advance checksum - - ByteString messageID = message.getMsg().getData(); // The data field contains the message ID - - checksum.update(messageID); - - } - - if (checksum.getChecksum() == currentQuery.getChecksum()){ - lastTimeOfSync = currentQuery.getTimeOfSync(); - } - - if (lastTimeOfSync == null){ - return SyncQueryResponse.newBuilder() - .setLastEntryNum(-1) - .setLastTimeOfSync(com.google.protobuf.Timestamp.getDefaultInstance()) - .build(); - } else{ - return SyncQueryResponse.newBuilder() - .setLastEntryNum(lastEntryNum) - .setLastTimeOfSync(lastTimeOfSync) - .build(); - } - - } - - - @Override - public void close() {} - -} +package meerkat.bulletinboard.sqlserver; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.sql.*; +import java.util.*; + +import com.google.protobuf.*; + +import com.google.protobuf.Timestamp; +import meerkat.bulletinboard.*; +import meerkat.bulletinboard.sqlserver.mappers.*; + +import meerkat.comm.CommunicationException; + +import meerkat.comm.MessageInputStream; +import meerkat.comm.MessageOutputStream; +import meerkat.crypto.DigitalSignature; +import meerkat.crypto.concrete.SHA256Digest; + +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Comm; +import meerkat.protobuf.Crypto.Signature; +import meerkat.protobuf.Crypto.SignatureVerificationKey; + + +import static meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider.*; + +import javax.sql.DataSource; + +import meerkat.util.BulletinBoardUtils; +import meerkat.util.TimestampComparator; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; + + + +/** + * This is a generic SQL implementation of the BulletinBoardServer API. + */ +public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{ + + /** + * This interface provides the required implementation-specific data to enable an access to an actual SQL server. + * It accounts for differences in languages between SQL DB types and for the different addresses needed to access them. + */ + public interface SQLQueryProvider { + + /** + * Allowed query types. + * Note that each query returned has to comply with the parameter names specified ny getParamNames + */ + public static enum QueryType { + + FIND_MSG_ID( + new String[] {"MsgId"}, + new int[] {Types.BLOB} + ), + + FIND_TAG_ID( + new String[] {"Tag"}, + new int[] {Types.VARCHAR} + ), + + INSERT_MSG( + new String[] {"MsgId","TimeStamp","Msg"}, + new int[] {Types.BLOB, Types.TIMESTAMP, Types.BLOB} + ), + + DELETE_MSG_BY_ENTRY( + new String[] {"EntryNum"}, + new int[] {Types.INTEGER} + ), + + DELETE_MSG_BY_ID( + new String[] {"MsgId"}, + new int[] {Types.BLOB} + ), + + INSERT_NEW_TAG( + new String[] {"Tag"}, + new int[] {Types.VARCHAR} + ), + + CONNECT_TAG( + new String[] {"EntryNum","Tag"}, + new int[] {Types.INTEGER, Types.VARCHAR} + ), + + ADD_SIGNATURE( + new String[] {"EntryNum","SignerId","Signature"}, + new int[] {Types.INTEGER, Types.BLOB, Types.BLOB} + ), + + GET_SIGNATURES( + new String[] {"EntryNum"}, + new int[] {Types.INTEGER} + ), + + GET_MESSAGES( + new String[] {}, + new int[] {} + ), + + COUNT_MESSAGES( + new String[] {}, + new int[] {} + ), + + GET_MESSAGE_STUBS( + new String[] {}, + new int[] {} + ), + + GET_LAST_MESSAGE_ENTRY( + new String[] {}, + new int[] {} + ), + + CHECK_BATCH_LENGTH( + new String[] {"BatchId"}, + new int[] {Types.BLOB, Types.INTEGER} + ), + + CHECK_BATCH_OPEN( + new String[] {"BatchId"}, + new int[] {Types.BLOB, Types.INTEGER} + ), + + GET_BATCH_MESSAGE_DATA_BY_MSG_ID( + new String[] {"MsgId", "StartPosition"}, + new int[] {Types.BLOB, Types.INTEGER} + ), + + GET_BATCH_MESSAGE_DATA_BY_BATCH_ID( + new String[] {"BatchId", "StartPosition"}, + new int[] {Types.INTEGER, Types.INTEGER} + ), + + INSERT_BATCH_DATA( + new String[] {"BatchId", "SerialNum", "Data"}, + new int[] {Types.INTEGER, Types.INTEGER, Types.BLOB} + ), + + STORE_BATCH_TAGS( + new String[] {"Tags"}, + new int[] {Types.BLOB} + ), + + GET_BATCH_TAGS( + new String[] {"BatchId"}, + new int[] {Types.BLOB, Types.INTEGER} + ), + + ADD_ENTRY_NUM_TO_BATCH( + new String[] {"BatchId", "EntryNum"}, + new int[] {Types.BLOB, Types.INTEGER, Types.INTEGER} + ); + + private String[] paramNames; + private int[] paramTypes; + + private QueryType(String[] paramNames, int[] paramTypes) { + this.paramNames = paramNames; + this.paramTypes = paramTypes; + } + + public String[] getParamNames() { + return paramNames; + } + + public String getParamName(int num) { + return paramNames[num]; + } + + public int[] getParamTypes() { + return paramTypes; + } + + public int getParamType(int num) { + return paramTypes[num]; + } + + } + + /** + * This enum provides the standard translation between a filter type and the corresponding parameter name in the SQL query + */ + public static enum FilterTypeParam { + + ENTRY_NUM("EntryNum", Types.INTEGER), + MSG_ID("MsgId", Types.BLOB), + SIGNER_ID("SignerId", Types.BLOB), + TAG("Tag", Types.VARCHAR), + LIMIT("Limit", Types.INTEGER), + TIMESTAMP("TimeStamp", Types.TIMESTAMP); + + private FilterTypeParam(String paramName, int paramType) { + this.paramName = paramName; + this.paramType = paramType; + } + + private String paramName; + private int paramType; + + public static FilterTypeParam getFilterTypeParamName(FilterType filterType) { + switch (filterType) { + + case MSG_ID: + return MSG_ID; + + case EXACT_ENTRY: // Go through + case MAX_ENTRY: // Go through + case MIN_ENTRY: + return ENTRY_NUM; + + case SIGNER_ID: + return SIGNER_ID; + + case TAG: + return TAG; + + case MAX_MESSAGES: + return LIMIT; + + case BEFORE_TIME: // Go through + case AFTER_TIME: + return TIMESTAMP; + + default: + return null; + } + } + + public String getParamName() { + return paramName; + } + + public int getParamType() { + return paramType; + } + } + + /** + * This function translates a QueryType into an actual SQL query. + * @param queryType is the type of query requested + * @return a string representation of the query for the specific type of SQL database implemented. + */ + public String getSQLString(QueryType queryType) throws IllegalArgumentException; + + /** + * Used to retrieve a condition to add to an SQL statement that will make the result comply with the filter type + * @param filterType is the filter type + * @param serialNum is a unique number used to identify the condition variables from other condition instances + * @return The SQL string for the condition + * @throws IllegalArgumentException if the filter type used is not supported + */ + public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException; + + public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException; + + /** + * @return the string needed in order to connect to the DB. + */ + public DataSource getDataSource(); + + /** + * This is used to get a list of queries that together create the schema needed for the DB. + * Note that these queries should not assume anything about the current state of the DB. + * In particular: they should not erase any existing tables and/or entries. + * @return The list of queries. + */ + public List getSchemaCreationCommands(); + + /** + * This is used to get a list of queries that together delete the schema needed for the DB. + * This is useful primarily for tests, in which we want to make sure we start with a clean DB. + * @return The list of queries. + */ + public List getSchemaDeletionCommands(); + + } + + /** + * This method returns the value of the parameter specified in a message filter + * @param messageFilter is the filter + * @return the object parameter for the SQL query embedded in the filter (this depends on the filter type) + */ + private Object getParam(MessageFilter messageFilter) { + + switch (messageFilter.getType()) { + + case MSG_ID: // Go through + case SIGNER_ID: + return messageFilter.getId().toByteArray(); + + case EXACT_ENTRY: // Go through + case MAX_ENTRY: // Go through + case MIN_ENTRY: + return messageFilter.getEntry(); + + case TAG: + return messageFilter.getTag(); + + case MAX_MESSAGES: + return messageFilter.getMaxMessages(); + + case BEFORE_TIME: // Go through + case AFTER_TIME: + return BulletinBoardUtils.toSQLTimestamp(messageFilter.getTimestamp()); + + default: // Unsupported filter type + return null; + } + + } + + /** + * This class implements a comparator for the MessageFilter class + * The comparison is done solely by comparing the type of the filter + * This is used to sort the filters by type + */ + public class FilterTypeComparator implements Comparator { + + @Override + public int compare(MessageFilter filter1, MessageFilter filter2) { + return filter1.getTypeValue() - filter2.getTypeValue(); + } + } + + protected SQLQueryProvider sqlQueryProvider; + + protected NamedParameterJdbcTemplate jdbcTemplate; + + protected BulletinBoardDigest digest; + protected DigitalSignature signer; + + protected List trusteeSignatureVerificationArray; + protected int minTrusteeSignatures; + + protected List> pollingCommitteeSignatureVerificationKeyArray; + protected int minCommiteeSignatures; + + /** + * This constructor sets the type of SQL language in use. + * @param sqlQueryProvider is the provider of the SQL query strings required for actual operation of the server. + */ + public BulletinBoardSQLServer(SQLQueryProvider sqlQueryProvider) { + this.sqlQueryProvider = sqlQueryProvider; + } + + /** + * This method creates the schema in the given DB to prepare for future transactions + * It does not assume anything about the current state of the database + * @throws SQLException + */ + private void createSchema() throws SQLException { + + for (String command : sqlQueryProvider.getSchemaCreationCommands()) { + jdbcTemplate.update(command,(Map) null); + } + + } + + /** + * This method initializes the signatures, connects to the DB and creates the schema (if required). + */ + @Override + public void init() throws CommunicationException { + // TODO write signature reading part. + + digest = new GenericBulletinBoardDigest(new SHA256Digest()); + + jdbcTemplate = new NamedParameterJdbcTemplate(sqlQueryProvider.getDataSource()); + + try { + createSchema(); + } catch (SQLException e) { + throw new CommunicationException("Couldn't create schema " + e.getMessage()); + } + + } + + /** + * This method verifies the authenticity of the received message based on + * the stored signatures. + * + * @param msg + * is the message to authenticate (containing the signature). + * @return TRUE if the message is authenticated and FALSE otherwise. + */ + private boolean verifyMessage(BulletinBoardMessage msg) { + //TODO: Replace with actual verification. + return true; + } + + /** + * This procedure makes sure that all tags in the given list have an entry in the tags table. + * @param tags + */ + protected void insertNewTags(String[] tags) throws SQLException { + + String sql; + + sql = sqlQueryProvider.getSQLString(QueryType.INSERT_NEW_TAG); + Map namedParameters[] = new HashMap[tags.length]; + + for (int i = 0 ; i < tags.length ; i++){ + namedParameters[i] = new HashMap(); + namedParameters[i].put(QueryType.INSERT_NEW_TAG.getParamName(0), tags[i]); + } + + jdbcTemplate.batchUpdate(sql, namedParameters); + + } + + /** + * This procedure is used to convert a boolean to a BoolValue. + * @param b is the boolean to convert. + * @return a ProtoBuf message with boolean payload. + */ + private BoolValue boolToBoolValue(boolean b){ + return BoolValue.newBuilder() + .setValue(b) + .build(); + } + + + /** + * This method posts a messages to the server + * @param msg is the message to post + * @param precalculatedMsgID is an optional precalculated message ID + * It is used when the message is the stub of a batch message + * In this case the validity of the signature is not checked either + * @return -1 if the message is not verified + * The entry number of the message if the message is posted + * @throws CommunicationException + */ + private long postMessage(BulletinBoardMessage msg, byte[] precalculatedMsgID) throws CommunicationException{ + + if (precalculatedMsgID != null && !verifyMessage(msg)) { + return -1; + } + + String sql; + Map[] namedParameterArray; + + byte[] msgID; + long entryNum; + + ProtocolStringList tagList; + String[] tags; + + List signatureList; + Signature[] signatures; + + + if (precalculatedMsgID != null){ + msgID = precalculatedMsgID; + } else{ + // Calculate message ID (depending only on the the unsigned message) + digest.reset(); + digest.update(msg.getMsg()); + + msgID = digest.digest(); + } + + // Add message to table if needed and store entry number of message. + + sql = sqlQueryProvider.getSQLString(QueryType.FIND_MSG_ID); + Map namedParameters = new HashMap(); + + namedParameters.put(QueryType.FIND_MSG_ID.getParamName(0),msgID); + + List entryNums = jdbcTemplate.query(sql, namedParameters, new LongMapper()); + + if (entryNums.size() > 0){ + + entryNum = entryNums.get(0); + + } else{ + + sql = sqlQueryProvider.getSQLString(QueryType.INSERT_MSG); + + namedParameters.put(QueryType.INSERT_MSG.getParamName(1), BulletinBoardUtils.toSQLTimestamp(msg.getMsg().getTimestamp())); + namedParameters.put(QueryType.INSERT_MSG.getParamName(2), msg.getMsg().toByteArray()); + + KeyHolder keyHolder = new GeneratedKeyHolder(); + jdbcTemplate.update(sql, new MapSqlParameterSource(namedParameters), keyHolder); + + entryNum = keyHolder.getKey().longValue(); + + } + + // Retrieve tags and store new ones in tag table. + + try { + + tagList = msg.getMsg().getTagList(); + tags = new String[tagList.size()]; + tags = tagList.toArray(tags); + + insertNewTags(tags); + + } catch (SQLException e) { + throw new CommunicationException(e.getMessage()); + } + + // Connect message to tags. + + sql = sqlQueryProvider.getSQLString(QueryType.CONNECT_TAG); + + namedParameterArray = new HashMap[tags.length]; + + for (int i = 0 ; i < tags.length ; i++) { + namedParameterArray[i] = new HashMap(); + namedParameterArray[i].put(QueryType.CONNECT_TAG.getParamName(0), entryNum); + namedParameterArray[i].put(QueryType.CONNECT_TAG.getParamName(1), tags[i]); + } + + jdbcTemplate.batchUpdate(sql, namedParameterArray); + + // Retrieve signatures. + + signatureList = msg.getSigList(); + signatures = new Signature[signatureList.size()]; + signatures = signatureList.toArray(signatures); + + // Connect message to signatures. + + sql = sqlQueryProvider.getSQLString(QueryType.ADD_SIGNATURE); + + namedParameterArray = new HashMap[signatures.length]; + + for (int i = 0 ; i < signatures.length ; i++) { + namedParameterArray[i] = new HashMap(); + namedParameterArray[i].put(QueryType.ADD_SIGNATURE.getParamName(0), entryNum); + namedParameterArray[i].put(QueryType.ADD_SIGNATURE.getParamName(1), signatures[i].getSignerId().toByteArray()); + namedParameterArray[i].put(QueryType.ADD_SIGNATURE.getParamName(2), signatures[i].toByteArray()); + } + + jdbcTemplate.batchUpdate(sql,namedParameterArray); + + return entryNum; + + } + + private void checkConnection() throws CommunicationException { + if (jdbcTemplate == null) { + throw new CommunicationException("DB connection not initialized"); + } + } + + @Override + public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException { + + checkConnection(); + + // Perform a post, calculate the message ID and check the signature for authenticity + if (postMessage(msg, null) != -1){ + return BoolValue.newBuilder().setValue(true).build(); // Message was posted + } + + return BoolValue.newBuilder().setValue(false).build(); // Message was not posted + + } + + @Override + public BoolValue deleteMessage(MessageID msgID) throws CommunicationException { + + checkConnection(); + + String sql = sqlQueryProvider.getSQLString(QueryType.DELETE_MSG_BY_ID); + Map namedParameters = new HashMap(); + + namedParameters.put(QueryType.DELETE_MSG_BY_ID.getParamName(0),msgID); + + int affectedRows = jdbcTemplate.update(sql, namedParameters); + + //TODO: Log + + return BoolValue.newBuilder().setValue(affectedRows > 0).build(); + + } + + @Override + public BoolValue deleteMessage(long entryNum) throws CommunicationException { + + checkConnection(); + + String sql = sqlQueryProvider.getSQLString(QueryType.DELETE_MSG_BY_ENTRY); + Map namedParameters = new HashMap(); + + namedParameters.put(QueryType.DELETE_MSG_BY_ENTRY.getParamName(0),entryNum); + + int affectedRows = jdbcTemplate.update(sql, namedParameters); + + //TODO: Log + + return BoolValue.newBuilder().setValue(affectedRows > 0).build(); + + } + + + /** + * This is a container class for and SQL string builder and a MapSqlParameterSource to be used with it + */ + class SQLAndParameters { + + public StringBuilder sql; + public MapSqlParameterSource parameters; + + public SQLAndParameters(int numOfFilters) { + sql = new StringBuilder(50 * numOfFilters); + parameters = new MapSqlParameterSource(); + } + + } + + SQLAndParameters getSQLFromFilters(MessageFilterList filterList) { + + SQLAndParameters result = new SQLAndParameters(filterList.getFilterCount()); + + List filters = new ArrayList(filterList.getFilterList()); + + Collections.sort(filters, new FilterTypeComparator()); + + boolean isFirstFilter = true; + + if (!filters.isEmpty()) { + result.sql.append(" WHERE "); + + for (int paramNum = 0 ; paramNum < filters.size() ; paramNum++) { + + MessageFilter filter = filters.get(paramNum); + + if (filter.getType().getNumber() != FilterType.MAX_MESSAGES_VALUE) { + if (isFirstFilter) { + isFirstFilter = false; + } else { + result.sql.append(" AND "); + } + } + + result.sql.append(sqlQueryProvider.getCondition(filter.getType(), paramNum)); + + FilterTypeParam filterTypeParam = FilterTypeParam.getFilterTypeParamName(filter.getType()); + + result.parameters.addValue( + filterTypeParam.getParamName() + Integer.toString(paramNum), + getParam(filter), + filterTypeParam.getParamType(), + sqlQueryProvider.getConditionParamTypeName(filter.getType())); + + } + + } + + return result; + + } + + /** + * Private implementation of the message stub reader for returning result as a list + * @param filterList is a filter list that defines which messages the client is interested in + * @return the requested list of message stubs + */ + private List readMessageStubs(MessageFilterList filterList) { + + StringBuilder sqlBuilder = new StringBuilder(50 * (filterList.getFilterCount() + 1)); + + sqlBuilder.append(sqlQueryProvider.getSQLString(QueryType.GET_MESSAGE_STUBS)); + + // Get Conditions + + SQLAndParameters sqlAndParameters = getSQLFromFilters(filterList); + + sqlBuilder.append(sqlAndParameters.sql); + + // Run query + + return jdbcTemplate.query(sqlBuilder.toString(), sqlAndParameters.parameters, new MessageStubMapper()); + + } + + /** + * Private implementation of the message reader for returning result as a list + * @param filterList is a filter list that defines which messages the client is interested in + * @return the requested list of messages + */ + private List readMessages(MessageFilterList filterList) throws CommunicationException { + + try { + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + readMessages(filterList, new MessageOutputStream(outputStream)); + + MessageInputStream inputStream = + MessageInputStream.MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream( + outputStream.toByteArray()), + BulletinBoardMessage.class); + + return inputStream.asList(); + + } catch (Exception e) { + throw new CommunicationException(e.getCause() + " " + e.getMessage()); + } + + } + + + @Override + public void readMessages(MessageFilterList filterList, MessageOutputStream out) throws CommunicationException { + + checkConnection(); + + BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder(); + + // SQL length is roughly 50 characters per filter + 50 for the query itself + StringBuilder sqlBuilder = new StringBuilder(50 * (filterList.getFilterCount() + 1)); + + // Check if Tag/Signature tables are required for filtering purposes + + sqlBuilder.append(sqlQueryProvider.getSQLString(QueryType.GET_MESSAGES)); + + // Get conditions + + SQLAndParameters sqlAndParameters = getSQLFromFilters(filterList); + sqlBuilder.append(sqlAndParameters.sql); + + // Run query and stream the output using a MessageCallbackHandler + + jdbcTemplate.query(sqlBuilder.toString(), sqlAndParameters.parameters, new MessageCallbackHandler(jdbcTemplate, sqlQueryProvider, out)); + + } + + @Override + public Int32Value getMessageCount(MessageFilterList filterList) throws CommunicationException { + + checkConnection(); + + BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder(); + + // SQL length is roughly 50 characters per filter + 50 for the query itself + StringBuilder sqlBuilder = new StringBuilder(50 * (filterList.getFilterCount() + 1)); + + // Check if Tag/Signature tables are required for filtering purposes + + sqlBuilder.append(sqlQueryProvider.getSQLString(QueryType.COUNT_MESSAGES)); + + // Get conditions + + SQLAndParameters sqlAndParameters = getSQLFromFilters(filterList); + sqlBuilder.append(sqlAndParameters.sql); + + // Run query and stream the output using a MessageCallbackHandler + + List count = jdbcTemplate.query(sqlBuilder.toString(), sqlAndParameters.parameters, new LongMapper()); + return Int32Value.newBuilder().setValue(count.get(0).intValue()).build(); + + } + + + /** + * This method checks if a specified batch exists and is already closed + * @param msgID is the unique ID of the batch message + * @return TRUE if the batch is closed and FALSE if it is still open or doesn't exist at all + */ + private boolean isBatchClosed(MessageID msgID) throws CommunicationException { + + MessageFilterList filterList = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.MSG_ID) + .setId(msgID.getID()) + .build()) + .build(); + + + List messages = readMessages(filterList); + + if (messages.size() <= 0){ + return false; + } + + return (messages.get(0).getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.MSGID); + + } + + /** + * This method checks if a specified batch exists and is still open + * @param batchId is the temporary batch ID + * @return TRUE if the batch is closed and FALSE if it is still open or doesn't exist at all + */ + private boolean isBatchOpen(long batchId) throws CommunicationException { + + String sql = sqlQueryProvider.getSQLString(QueryType.CHECK_BATCH_OPEN); + MapSqlParameterSource namedParameters = new MapSqlParameterSource(); + + namedParameters.addValue(QueryType.CHECK_BATCH_OPEN.getParamName(0),batchId); + + List result = jdbcTemplate.query(sql, namedParameters, new LongMapper()); + + return (result.size() > 0 && result.get(0) > 0); + + } + + @Override + public Int64Value beginBatch(BeginBatchMessage message) throws CommunicationException { + + checkConnection(); + + // Store tags + String sql = sqlQueryProvider.getSQLString(QueryType.STORE_BATCH_TAGS); + MapSqlParameterSource namedParameters = new MapSqlParameterSource(); + + namedParameters.addValue(QueryType.STORE_BATCH_TAGS.getParamName(0),message.toByteArray()); + + jdbcTemplate.update(sql,namedParameters); + + KeyHolder keyHolder = new GeneratedKeyHolder(); + jdbcTemplate.update(sql, namedParameters, keyHolder); + + long entryNum = keyHolder.getKey().longValue(); + + return Int64Value.newBuilder().setValue(entryNum).build(); + + } + + + @Override + public BoolValue postBatchMessage(BatchMessage batchMessage) throws CommunicationException{ + + checkConnection(); + + // Make sure batch is open + if (!isBatchOpen(batchMessage.getBatchId())) { + return BoolValue.newBuilder().setValue(false).build(); + } + + // Add data + String sql = sqlQueryProvider.getSQLString(QueryType.INSERT_BATCH_DATA); + MapSqlParameterSource namedParameters = new MapSqlParameterSource(); + + namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(0),batchMessage.getBatchId()); + namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(1),batchMessage.getSerialNum()); + namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(2),batchMessage.getData().toByteArray()); + + jdbcTemplate.update(sql, namedParameters); + + return BoolValue.newBuilder().setValue(true).build(); + + } + + + @Override + public BoolValue closeBatch(CloseBatchMessage message) throws CommunicationException { + + checkConnection(); + + // Check batch size + + String sql = sqlQueryProvider.getSQLString(QueryType.CHECK_BATCH_LENGTH); + MapSqlParameterSource namedParameters = new MapSqlParameterSource(); + + + namedParameters.addValue(QueryType.CHECK_BATCH_LENGTH.getParamName(0),message.getBatchId()); + + List lengthResult = jdbcTemplate.query(sql, namedParameters, new LongMapper()); + + if (lengthResult.get(0) != message.getBatchLength()) { + return BoolValue.newBuilder().setValue(false).build(); + } + + // Get Tags and add them to BulletinBoardMessage + + sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_TAGS); + namedParameters = new MapSqlParameterSource(); + + namedParameters.addValue(QueryType.GET_BATCH_TAGS.getParamName(0),message.getBatchId()); + + List beginBatchMessages = jdbcTemplate.query(sql, namedParameters, new BeginBatchMessageMapper()); + + if (beginBatchMessages == null || beginBatchMessages.size() <= 0 || beginBatchMessages.get(0) == null) { + return BoolValue.newBuilder().setValue(false).build(); + } + + UnsignedBulletinBoardMessage unsignedMessage = UnsignedBulletinBoardMessage.newBuilder() + .addAllTag(beginBatchMessages.get(0).getTagList()) + .setTimestamp(message.getTimestamp()) + .build(); + + // Digest the data + + digest.reset(); + digest.update(unsignedMessage); + + sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID); + namedParameters = new MapSqlParameterSource(); + + namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(0),message.getBatchId()); + namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(1),0); // Read from the beginning + jdbcTemplate.query(sql, namedParameters, new BatchDataDigestHandler(digest)); + + byte[] msgID = digest.digest(); + + //TODO: Signature verification + + // Create Bulletin Board message + BulletinBoardMessage bulletinBoardMessage = BulletinBoardMessage.newBuilder() + .setMsg(UnsignedBulletinBoardMessage.newBuilder() + .mergeFrom(unsignedMessage) + .setMsgId(ByteString.copyFrom(msgID))) + .addAllSig(message.getSigList()) + .build(); + + // Post message with pre-calculated ID and without checking signature validity + long entryNum = postMessage(bulletinBoardMessage, msgID); + + // Add entry num to tag data table + sql = sqlQueryProvider.getSQLString(QueryType.ADD_ENTRY_NUM_TO_BATCH); + namedParameters = new MapSqlParameterSource(); + + namedParameters.addValue(QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(0), message.getBatchId()); + namedParameters.addValue(QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1), entryNum); + + jdbcTemplate.update(sql, namedParameters); + + // Return TRUE + + return BoolValue.newBuilder().setValue(true).build(); + + } + + + @Override + public void readBatch(BatchQuery batchQuery, MessageOutputStream out) throws CommunicationException, IllegalArgumentException{ + + checkConnection(); + + // Check that batch is closed + if (!isBatchClosed(batchQuery.getMsgID())) { + throw new IllegalArgumentException("No such batch"); + } + + String sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID); + MapSqlParameterSource namedParameters = new MapSqlParameterSource(); + + namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(0),batchQuery.getMsgID().getID().toByteArray()); + namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(1),batchQuery.getStartPosition()); + + jdbcTemplate.query(sql, namedParameters, new BatchDataCallbackHandler(out)); + + } + + /** + * Finds the entry number of the last entry in the database + * @return the entry number, or -1 if no entries are found + */ + protected long getLastMessageEntry() { + + String sql = sqlQueryProvider.getSQLString(QueryType.GET_LAST_MESSAGE_ENTRY); + + List resultList = jdbcTemplate.query(sql, new LongMapper()); + + if (resultList.size() <= 0){ + return -1; + } + + return resultList.get(0); + + } + + @Override + public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException{ + + checkConnection(); + + if (generateSyncQueryParams == null + || !generateSyncQueryParams.hasFilterList() + || generateSyncQueryParams.getFilterList().getFilterCount() <= 0 + || generateSyncQueryParams.getBreakpointListCount() <= 0){ + + return SyncQuery.getDefaultInstance(); + + } + + List messages = readMessageStubs(generateSyncQueryParams.getFilterList()); + + if (messages.size() <= 0){ + return SyncQuery.newBuilder().build(); + } + + SyncQuery.Builder resultBuilder = SyncQuery.newBuilder(); + + Iterator messageIterator = messages.iterator(); + Iterator breakpointIterator = generateSyncQueryParams.getBreakpointListList().iterator(); + + Checksum checksum = new SimpleChecksum(); + checksum.setDigest(new SHA256Digest()); + + Timestamp lastTimestamp = Timestamp.getDefaultInstance(); + BulletinBoardMessage message = messageIterator.next(); + long currentMessageNum = 1; + + boolean checksumChanged = true; + + while (breakpointIterator.hasNext()){ + + Float breakpoint = breakpointIterator.next(); + + // Continue while breakpoint not reached, or it has been reached but no new timestamp has been encountered since + while ( messageIterator.hasNext() + && ((float) currentMessageNum / (float) messages.size() <= breakpoint) + || ((float) currentMessageNum / (float) messages.size() > breakpoint + && lastTimestamp.equals(message.getMsg().getTimestamp()))){ + + checksumChanged = true; + + checksum.update(message.getMsg().getMsgId()); + + lastTimestamp = message.getMsg().getTimestamp(); + message = messageIterator.next(); + + } + + if (checksumChanged) { + + checksum.update(message.getMsg().getData()); + resultBuilder.addQuery(SingleSyncQuery.newBuilder() + .setTimeOfSync(message.getMsg().getTimestamp()) + .setChecksum(checksum.getChecksum()) + .build()); + + } + + checksumChanged = false; + + } + + return resultBuilder.build(); + + } + + + /** + * Searches for the latest time of sync of the DB relative to a given query and returns the metadata needed to complete the sync + * The checksum up to (and including) each given timestamp is calculated using an instance of SimpleChecksum + * @param syncQuery contains a succinct representation of states to compare against + * @return the current last entry num and latest time of sync if there is one; -1 as last entry and empty timestamp otherwise + * @throws CommunicationException + */ + @Override + public SyncQueryResponse querySync(SyncQuery syncQuery) throws CommunicationException { + + checkConnection(); + + if (syncQuery == null){ + return SyncQueryResponse.newBuilder() + .setLastEntryNum(-1) + .setLastTimeOfSync(com.google.protobuf.Timestamp.getDefaultInstance()) + .build(); + } + + com.google.protobuf.Timestamp lastTimeOfSync = null; + + TimestampComparator timestampComparator = new TimestampComparator(); + + long lastEntryNum = getLastMessageEntry(); + + Iterator queryIterator = syncQuery.getQueryList().iterator(); + + SingleSyncQuery currentQuery = queryIterator.next(); + + List messageStubs = readMessageStubs(syncQuery.getFilterList()); + + Checksum checksum = new SimpleChecksum(); + + for (BulletinBoardMessage message : messageStubs){ + + // Check for end of current query + if (timestampComparator.compare(message.getMsg().getTimestamp(), currentQuery.getTimeOfSync()) > 0){ + + if (checksum.getChecksum() == currentQuery.getChecksum()){ + lastTimeOfSync = currentQuery.getTimeOfSync(); + } else { + break; + } + + if (queryIterator.hasNext()){ + currentQuery = queryIterator.next(); + } else{ + break; + } + + } + + // Advance checksum + + ByteString messageID = message.getMsg().getMsgId(); // The data field contains the message ID + + checksum.update(messageID); + + } + + if (checksum.getChecksum() == currentQuery.getChecksum()){ + lastTimeOfSync = currentQuery.getTimeOfSync(); + } + + if (lastTimeOfSync == null){ + return SyncQueryResponse.newBuilder() + .setLastEntryNum(-1) + .setLastTimeOfSync(com.google.protobuf.Timestamp.getDefaultInstance()) + .build(); + } else{ + return SyncQueryResponse.newBuilder() + .setLastEntryNum(lastEntryNum) + .setLastTimeOfSync(lastTimeOfSync) + .build(); + } + + } + + + @Override + public void close() { + jdbcTemplate = null; + } + +} diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java index 74b509b..872e226 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java @@ -1,266 +1,291 @@ -package meerkat.bulletinboard.sqlserver; - -import meerkat.protobuf.BulletinBoardAPI.FilterType; -import org.apache.commons.dbcp2.BasicDataSource; -import org.h2.jdbcx.JdbcDataSource; -import javax.naming.Context; -import javax.naming.InitialContext; - -import javax.naming.NamingException; -import javax.sql.DataSource; -import java.text.MessageFormat; -import java.util.LinkedList; -import java.util.List; - -/** - * Created by Arbel Deutsch Peled on 09-Dec-15. - */ - -public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider { - - private String dbName; - - public H2QueryProvider(String dbName) { - this.dbName = dbName; - } - - - @Override - public String getSQLString(QueryType queryType) throws IllegalArgumentException{ - - switch(queryType) { - case ADD_SIGNATURE: - return "INSERT INTO SignatureTable (EntryNum, SignerId, Signature)" - + " SELECT DISTINCT :EntryNum AS Entry, :SignerId AS Id, :Signature AS Sig FROM UtilityTable AS Temp" - + " WHERE NOT EXISTS" - + " (SELECT 1 FROM SignatureTable AS SubTable WHERE SubTable.SignerId = :SignerId AND SubTable.EntryNum = :EntryNum)"; - - case CONNECT_TAG: - return "INSERT INTO MsgTagTable (TagId, EntryNum)" - + " SELECT DISTINCT TagTable.TagId, :EntryNum AS NewEntry FROM TagTable WHERE Tag = :Tag" - + " AND NOT EXISTS (SELECT 1 FROM MsgTagTable AS SubTable WHERE SubTable.TagId = TagTable.TagId" - + " AND SubTable.EntryNum = :EntryNum)"; - - case FIND_MSG_ID: - return "SELECT EntryNum From MsgTable WHERE MsgId = :MsgId"; - - case FIND_TAG_ID: - return MessageFormat.format( - "SELECT TagId FROM TagTable WHERE Tag = :{0}", - QueryType.FIND_TAG_ID.getParamName(0)); - - case GET_MESSAGES: - return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"; - - case COUNT_MESSAGES: - return "SELECT COUNT(MsgTable.EntryNum) FROM MsgTable"; - - case GET_MESSAGE_STUBS: - return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable"; - - case GET_SIGNATURES: - return "SELECT Signature FROM SignatureTable WHERE EntryNum = :EntryNum"; - - case INSERT_MSG: - return "INSERT INTO MsgTable (MsgId, Msg, ExactTime) VALUES(:MsgId,:Msg,:TimeStamp)"; - - case INSERT_NEW_TAG: - return "INSERT INTO TagTable(Tag) SELECT DISTINCT :Tag AS NewTag FROM UtilityTable WHERE" - + " NOT EXISTS (SELECT 1 FROM TagTable AS SubTable WHERE SubTable.Tag = :Tag)"; - - case GET_LAST_MESSAGE_ENTRY: - return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable"; - - case GET_BATCH_MESSAGE_ENTRY: - return MessageFormat.format( - "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable" - + " INNER JOIN SignatureTable ON MsgTable.EntryNum = SignatureTable.EntryNum" - + " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum" - + " INNER JOIN TagTable ON MsgTagTable.TagId = TagTable.TagId" - + " WHERE SignatureTable.SignerId = :{0}" - + " AND TagTable.Tag = :{1}", - QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0), - QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1)); - - case GET_BATCH_MESSAGE_DATA: - return MessageFormat.format( - "SELECT Data FROM BatchTable" - + " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}" - + " ORDER BY SerialNum ASC", - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0), - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1), - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2)); - - case INSERT_BATCH_DATA: - return MessageFormat.format( - "INSERT INTO BatchTable (SignerId, BatchId, SerialNum, Data)" - + " VALUES (:{0}, :{1}, :{2}, :{3})", - QueryType.INSERT_BATCH_DATA.getParamName(0), - QueryType.INSERT_BATCH_DATA.getParamName(1), - QueryType.INSERT_BATCH_DATA.getParamName(2), - QueryType.INSERT_BATCH_DATA.getParamName(3)); - - case CHECK_BATCH_LENGTH: - return MessageFormat.format( - "SELECT COUNT(Data) AS BatchLength FROM BatchTable" - + " WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.CHECK_BATCH_LENGTH.getParamName(0), - QueryType.CHECK_BATCH_LENGTH.getParamName(1)); - - case CONNECT_BATCH_TAG: - return MessageFormat.format( - "INSERT INTO BatchTagTable (SignerId, BatchId, TagId) SELECT :{0}, :{1}, TagId FROM TagTable" - + " WHERE Tag = :{2}", - QueryType.CONNECT_BATCH_TAG.getParamName(0), - QueryType.CONNECT_BATCH_TAG.getParamName(1), - QueryType.CONNECT_BATCH_TAG.getParamName(2)); - - case GET_BATCH_TAGS: - return MessageFormat.format( - "SELECT Tag FROM TagTable INNER JOIN BatchTagTable ON TagTable.TagId = BatchTagTable.TagId" - + " WHERE SignerId = :{0} AND BatchId = :{1} ORDER BY Tag ASC", - QueryType.GET_BATCH_TAGS.getParamName(0), - QueryType.GET_BATCH_TAGS.getParamName(1)); - - case REMOVE_BATCH_TAGS: - return MessageFormat.format( - "DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.REMOVE_BATCH_TAGS.getParamName(0), - QueryType.REMOVE_BATCH_TAGS.getParamName(1)); - - default: - throw new IllegalArgumentException("Cannot serve a query of type " + queryType); - } - - } - - @Override - public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException { - - String serialString = Integer.toString(serialNum); - - switch(filterType) { - case EXACT_ENTRY: - return "MsgTable.EntryNum = :EntryNum" + serialString; - case MAX_ENTRY: - return "MsgTable.EntryNum <= :EntryNum" + serialString; - case MIN_ENTRY: - return "MsgTable.EntryNum >= :EntryNum" + serialString; - case MAX_MESSAGES: - return "LIMIT :Limit" + serialString; - case MSG_ID: - return "MsgTable.MsgId = :MsgId" + serialString; - case SIGNER_ID: - return "EXISTS (SELECT 1 FROM SignatureTable" - + " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)"; - case TAG: - return "EXISTS (SELECT 1 FROM TagTable" - + " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId" - + " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)"; - - case BEFORE_TIME: - return "MsgTable.ExactTime <= :TimeStamp" + serialString; - - case AFTER_TIME: - return "MsgTable.ExactTime >= :TimeStamp" + serialString; - - default: - throw new IllegalArgumentException("Cannot serve a filter of type " + filterType); - } - - } - - @Override - public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException { - - switch(filterType) { - case EXACT_ENTRY: // Go through - case MAX_ENTRY: // Go through - case MIN_ENTRY: // Go through - case MAX_MESSAGES: - return "INT"; - - case MSG_ID: // Go through - case SIGNER_ID: - return "TINYBLOB"; - - case TAG: - return "VARCHAR"; - - case AFTER_TIME: // Go through - case BEFORE_TIME: - return "TIMESTAMP"; - - - default: - throw new IllegalArgumentException("Cannot serve a filter of type " + filterType); - } - - } - - @Override - public DataSource getDataSource() { - - BasicDataSource dataSource = new BasicDataSource(); - - dataSource.setDriverClassName("org.h2.Driver"); - dataSource.setUrl("jdbc:h2:~/" + dbName); - - return dataSource; - - } - - - @Override - public List getSchemaCreationCommands() { - List list = new LinkedList(); - - list.add("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INT NOT NULL AUTO_INCREMENT PRIMARY KEY, MsgId TINYBLOB UNIQUE, ExactTime TIMESTAMP, Msg BLOB)"); - - list.add("CREATE TABLE IF NOT EXISTS 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)," - + " UNIQUE (EntryNum, TagID))"); - - list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INT, SignerId TINYBLOB, Signature TINYBLOB UNIQUE," - + " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))"); - - list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)"); - list.add("CREATE UNIQUE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId, EntryNum)"); - - list.add("CREATE TABLE IF NOT EXISTS BatchTable (SignerId TINYBLOB, BatchId INT, SerialNum INT, Data BLOB," - + " UNIQUE(SignerId, BatchId, SerialNum))"); - - list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, TagId INT," - + " FOREIGN KEY (TagId) REFERENCES TagTable(TagId))"); - - list.add("CREATE INDEX IF NOT EXISTS BatchIndex ON BatchTagTable(SignerId, BatchId)"); - - // This is used to create a simple table with one entry. - // It is used for implementing a workaround for the missing INSERT IGNORE syntax - list.add("CREATE TABLE IF NOT EXISTS UtilityTable (Entry INT)"); - list.add("INSERT INTO UtilityTable (Entry) VALUES (1)"); - - return list; - } - - @Override - public List getSchemaDeletionCommands() { - List list = new LinkedList(); - - list.add("DROP TABLE IF EXISTS UtilityTable"); - list.add("DROP INDEX IF EXISTS BatchIndex"); - list.add("DROP TABLE IF EXISTS BatchTagTable"); - list.add("DROP TABLE IF EXISTS BatchTable"); - list.add("DROP INDEX IF EXISTS SignerIdIndex"); - list.add("DROP TABLE IF EXISTS MsgTagTable"); - list.add("DROP TABLE IF EXISTS SignatureTable"); - list.add("DROP TABLE IF EXISTS TagTable"); - list.add("DROP TABLE IF EXISTS MsgTable"); - - return list; - } - -} +package meerkat.bulletinboard.sqlserver; + +import meerkat.protobuf.BulletinBoardAPI.FilterType; +import org.apache.commons.dbcp2.BasicDataSource; + +import javax.sql.DataSource; +import java.text.MessageFormat; +import java.util.LinkedList; +import java.util.List; + +/** + * Created by Arbel Deutsch Peled on 09-Dec-15. + */ + +public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider { + + private String dbName; + + public H2QueryProvider(String dbName) { + this.dbName = dbName; + } + + + @Override + public String getSQLString(QueryType queryType) throws IllegalArgumentException{ + + switch(queryType) { + case ADD_SIGNATURE: + return MessageFormat.format( + "INSERT INTO SignatureTable (EntryNum, SignerId, Signature)" + + " SELECT DISTINCT :{0} AS Entry, :{1} AS Id, :{2} AS Sig FROM UtilityTable AS Temp" + + " WHERE NOT EXISTS" + + " (SELECT 1 FROM SignatureTable AS SubTable WHERE SubTable.EntryNum = :{0} AND SubTable.SignerId = :{1})", + QueryType.ADD_SIGNATURE.getParamName(0), + QueryType.ADD_SIGNATURE.getParamName(1), + QueryType.ADD_SIGNATURE.getParamName(2)); + + case CONNECT_TAG: + return MessageFormat.format( + "INSERT INTO MsgTagTable (TagId, EntryNum)" + + " SELECT DISTINCT TagTable.TagId, :{0} AS NewEntry FROM TagTable WHERE Tag = :{1}" + + " AND NOT EXISTS (SELECT 1 FROM MsgTagTable AS SubTable WHERE SubTable.TagId = TagTable.TagId" + + " AND SubTable.EntryNum = :{0})", + QueryType.CONNECT_TAG.getParamName(0), + QueryType.CONNECT_TAG.getParamName(1)); + + case FIND_MSG_ID: + return MessageFormat.format( + "SELECT EntryNum From MsgTable WHERE MsgId = :{0}", + QueryType.FIND_MSG_ID.getParamName(0)); + + case FIND_TAG_ID: + return MessageFormat.format( + "SELECT TagId FROM TagTable WHERE Tag = :{0}", + QueryType.FIND_TAG_ID.getParamName(0)); + + case GET_MESSAGES: + return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"; + + case COUNT_MESSAGES: + return "SELECT COUNT(MsgTable.EntryNum) FROM MsgTable"; + + case GET_MESSAGE_STUBS: + return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable"; + + case GET_SIGNATURES: + return MessageFormat.format( + "SELECT Signature FROM SignatureTable WHERE EntryNum = :{0}", + QueryType.GET_SIGNATURES.getParamName(0)); + + case INSERT_MSG: + return MessageFormat.format( + "INSERT INTO MsgTable (MsgId, ExactTime, Msg) VALUES(:{0}, :{1}, :{2})", + QueryType.INSERT_MSG.getParamName(0), + QueryType.INSERT_MSG.getParamName(1), + QueryType.INSERT_MSG.getParamName(2)); + + case DELETE_MSG_BY_ENTRY: + return MessageFormat.format( + "DELETE FROM MsgTable WHERE EntryNum = :{0}", + QueryType.DELETE_MSG_BY_ENTRY.getParamName(0)); + + case DELETE_MSG_BY_ID: + return MessageFormat.format( + "DELETE FROM MsgTable WHERE MsgId = :{0}", + QueryType.DELETE_MSG_BY_ID.getParamName(0)); + + case INSERT_NEW_TAG: + return MessageFormat.format( + "INSERT INTO TagTable(Tag) SELECT DISTINCT :Tag AS NewTag FROM UtilityTable WHERE" + + " NOT EXISTS (SELECT 1 FROM TagTable AS SubTable WHERE SubTable.Tag = :{0})", + QueryType.INSERT_NEW_TAG.getParamName(0)); + + case GET_LAST_MESSAGE_ENTRY: + return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable"; + + case GET_BATCH_MESSAGE_DATA_BY_MSG_ID: + return MessageFormat.format( + "SELECT Data FROM BatchTable" + + " INNER JOIN MsgTable ON MsgTable.EntryNum = BatchTable.EntryNum" + + " WHERE MsgTable.MsgId = :{0} AND BatchTable.SerialNum >= :{1}" + + " ORDER BY BatchTable.SerialNum ASC", + QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(0), + QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(1)); + + case GET_BATCH_MESSAGE_DATA_BY_BATCH_ID: + return MessageFormat.format( + "SELECT Data FROM BatchTable" + + " WHERE BatchId = :{0} AND SerialNum >= :{1}" + + " ORDER BY BatchTable.SerialNum ASC", + QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(0), + QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(1)); + + case INSERT_BATCH_DATA: + return MessageFormat.format( + "INSERT INTO BatchTable (BatchId, SerialNum, Data) VALUES (:{0}, :{1}, :{2})", + QueryType.INSERT_BATCH_DATA.getParamName(0), + QueryType.INSERT_BATCH_DATA.getParamName(1), + QueryType.INSERT_BATCH_DATA.getParamName(2)); + + case CHECK_BATCH_LENGTH: + return MessageFormat.format( + "SELECT COUNT(Data) AS BatchLength FROM BatchTable WHERE BatchId = :{0}", + QueryType.CHECK_BATCH_LENGTH.getParamName(0)); + + case CHECK_BATCH_OPEN: + return MessageFormat.format( + "SELECT COUNT(BatchId) AS batchCount FROM BatchTagTable WHERE BatchId = :{0}", + QueryType.CHECK_BATCH_OPEN.getParamName(0)); + + case STORE_BATCH_TAGS: + return MessageFormat.format( + "INSERT INTO BatchTagTable (Tags) VALUES (:{0})", + QueryType.STORE_BATCH_TAGS.getParamName(0)); + + case GET_BATCH_TAGS: + return MessageFormat.format( + "SELECT Tags FROM BatchTagTable WHERE BatchId = :{0}", + QueryType.GET_BATCH_TAGS.getParamName(0)); + + case ADD_ENTRY_NUM_TO_BATCH: + return MessageFormat.format( + "UPDATE BatchTable SET EntryNum = :{1} WHERE BatchId = :{0}", + QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(0), + QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1)); + + default: + throw new IllegalArgumentException("Cannot serve a query of type " + queryType); + } + + } + + @Override + public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException { + + String serialString = Integer.toString(serialNum); + + switch(filterType) { + case EXACT_ENTRY: + return "MsgTable.EntryNum = :EntryNum" + serialString; + case MAX_ENTRY: + return "MsgTable.EntryNum <= :EntryNum" + serialString; + case MIN_ENTRY: + return "MsgTable.EntryNum >= :EntryNum" + serialString; + case MAX_MESSAGES: + return "LIMIT :Limit" + serialString; + case MSG_ID: + return "MsgTable.MsgId = :MsgId" + serialString; + case SIGNER_ID: + return "EXISTS (SELECT 1 FROM SignatureTable" + + " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)"; + case TAG: + return "EXISTS (SELECT 1 FROM TagTable" + + " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId" + + " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)"; + + case BEFORE_TIME: + return "MsgTable.ExactTime <= :TimeStamp" + serialString; + + case AFTER_TIME: + return "MsgTable.ExactTime >= :TimeStamp" + serialString; + + default: + throw new IllegalArgumentException("Cannot serve a filter of type " + filterType); + } + + } + + @Override + public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException { + + switch(filterType) { + case EXACT_ENTRY: // Go through + case MAX_ENTRY: // Go through + case MIN_ENTRY: // Go through + case MAX_MESSAGES: + return "INT"; + + case MSG_ID: // Go through + case SIGNER_ID: + return "TINYBLOB"; + + case TAG: + return "VARCHAR"; + + case AFTER_TIME: // Go through + case BEFORE_TIME: + return "TIMESTAMP"; + + + default: + throw new IllegalArgumentException("Cannot serve a filter of type " + filterType); + } + + } + + @Override + public DataSource getDataSource() { + + BasicDataSource dataSource = new BasicDataSource(); + + dataSource.setDriverClassName("org.h2.Driver"); + dataSource.setUrl("jdbc:h2:mem:" + dbName); + + return dataSource; + + } + + + @Override + public List getSchemaCreationCommands() { + List list = new LinkedList(); + + list.add("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INT NOT NULL AUTO_INCREMENT PRIMARY KEY," + + " MsgId TINYBLOB UNIQUE, ExactTime TIMESTAMP, Msg BLOB)"); + + list.add("CREATE TABLE IF NOT EXISTS 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) 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) ON DELETE CASCADE)"); + + list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)"); + list.add("CREATE UNIQUE INDEX IF NOT EXISTS SignatureIndex ON SignatureTable(SignerId, EntryNum)"); + + list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (BatchId INT AUTO_INCREMENT PRIMARY KEY, Tags BLOB)"); + + list.add("CREATE TABLE IF NOT EXISTS BatchTable (BatchId INT, EntryNum INT, SerialNum INT, Data BLOB," + + " UNIQUE(BatchId, SerialNum)," + + " FOREIGN KEY (BatchId) REFERENCES BatchTagTable(BatchId) ON DELETE CASCADE)"); + list.add("CREATE INDEX IF NOT EXISTS BatchDataIndex ON BatchTable(EntryNum, SerialNum)"); + + + + // This is used to create a simple table with one entry. + // It is used for implementing a workaround for the missing INSERT IGNORE syntax + list.add("CREATE TABLE IF NOT EXISTS UtilityTable (Entry INT)"); + list.add("INSERT INTO UtilityTable (Entry) VALUES (1)"); + + return list; + } + + @Override + public List getSchemaDeletionCommands() { + List list = new LinkedList(); + + list.add("DROP TABLE IF EXISTS UtilityTable"); + + list.add("DROP INDEX IF EXISTS BatchDataIndex"); + list.add("DROP TABLE IF EXISTS BatchTable"); + + list.add("DROP INDEX IF EXISTS BatchTagIndex"); + list.add("DROP TABLE IF EXISTS BatchTagTable"); + + list.add("DROP TABLE IF EXISTS MsgTagTable"); + + list.add("DROP INDEX IF EXISTS SignerIdIndex"); + list.add("DROP TABLE IF EXISTS SignatureTable"); + + list.add("DROP TABLE IF EXISTS TagTable"); + + list.add("DROP TABLE IF EXISTS MsgTable"); + + return list; + } + +} diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java index c8e6b67..097095f 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java @@ -1,273 +1,276 @@ -package meerkat.bulletinboard.sqlserver; - -import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; -import meerkat.bulletinboard.BulletinBoardConstants; -import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider; -import meerkat.protobuf.BulletinBoardAPI.FilterType; -import org.apache.commons.dbcp2.BasicDataSource; - -import javax.sql.DataSource; -import java.text.MessageFormat; -import java.util.LinkedList; -import java.util.List; - -/** - * Created by Arbel Deutsch Peled on 09-Dec-15. - */ - -public class MySQLQueryProvider implements SQLQueryProvider { - - private String dbAddress; - private int dbPort; - private String dbName; - private String username; - private String password; - - public MySQLQueryProvider(String dbAddress, int dbPort, String dbName, String username, String password) { - this.dbAddress = dbAddress; - this.dbPort = dbPort; - this.dbName = dbName; - this.username = username; - this.password = password; - } - - @Override - public String getSQLString(QueryType queryType) throws IllegalArgumentException{ - - switch(queryType) { - - case ADD_SIGNATURE: - return MessageFormat.format( - "INSERT IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (:{0}, :{1}, :{2})", - QueryType.ADD_SIGNATURE.getParamName(0), - QueryType.ADD_SIGNATURE.getParamName(1), - QueryType.ADD_SIGNATURE.getParamName(2)); - - case CONNECT_TAG: - return MessageFormat.format( - "INSERT IGNORE INTO MsgTagTable (TagId, EntryNum)" - + " SELECT TagTable.TagId, :{0} AS EntryNum FROM TagTable WHERE Tag = :{1}", - QueryType.CONNECT_TAG.getParamName(0), - QueryType.CONNECT_TAG.getParamName(1)); - - case FIND_MSG_ID: - return MessageFormat.format( - "SELECT EntryNum From MsgTable WHERE MsgId = :{0}", - QueryType.FIND_MSG_ID.getParamName(0)); - - case FIND_TAG_ID: - return MessageFormat.format( - "SELECT TagId FROM TagTable WHERE Tag = :{0}", - QueryType.FIND_TAG_ID.getParamName(0)); - - case GET_MESSAGES: - return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"; - - case COUNT_MESSAGES: - return "SELECT COUNT(MsgTable.EntryNum) FROM MsgTable"; - - case GET_MESSAGE_STUBS: - return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable"; - - case GET_SIGNATURES: - return MessageFormat.format( - "SELECT Signature FROM SignatureTable WHERE EntryNum = :{0}", - QueryType.GET_SIGNATURES.getParamName(0)); - - case INSERT_MSG: - return MessageFormat.format( - "INSERT INTO MsgTable (MsgId, ExactTime, Msg) VALUES(:{0}, :{1}, :{2})", - QueryType.INSERT_MSG.getParamName(0), - QueryType.INSERT_MSG.getParamName(1), - QueryType.INSERT_MSG.getParamName(2)); - - case INSERT_NEW_TAG: - return MessageFormat.format( - "INSERT IGNORE INTO TagTable(Tag) VALUES (:{0})", - QueryType.INSERT_NEW_TAG.getParamName(0)); - - case GET_LAST_MESSAGE_ENTRY: - return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable"; - - case GET_BATCH_MESSAGE_ENTRY: - return MessageFormat.format( - "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable" - + " INNER JOIN SignatureTable ON MsgTable.EntryNum = SignatureTable.EntryNum" - + " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum" - + " INNER JOIN TagTable ON MsgTagTable.TagId = TagTable.TagId" - + " WHERE SignatureTable.SignerId = :{0}" - + " AND TagTable.Tag = :{1}", - QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0), - QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1)); - - case GET_BATCH_MESSAGE_DATA: - return MessageFormat.format( - "SELECT Data FROM BatchTable" - + " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}" - + " ORDER BY SerialNum ASC", - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0), - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1), - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2)); - - case INSERT_BATCH_DATA: - return MessageFormat.format( - "INSERT INTO BatchTable (SignerId, BatchId, SerialNum, Data)" - + " VALUES (:{0}, :{1}, :{2}, :{3})", - QueryType.INSERT_BATCH_DATA.getParamName(0), - QueryType.INSERT_BATCH_DATA.getParamName(1), - QueryType.INSERT_BATCH_DATA.getParamName(2), - QueryType.INSERT_BATCH_DATA.getParamName(3)); - - case CHECK_BATCH_LENGTH: - return MessageFormat.format( - "SELECT COUNT(Data) AS BatchLength FROM BatchTable" - + " WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.CHECK_BATCH_LENGTH.getParamName(0), - QueryType.CHECK_BATCH_LENGTH.getParamName(1)); - - case CONNECT_BATCH_TAG: - return MessageFormat.format( - "INSERT INTO BatchTagTable (SignerId, BatchId, TagId) SELECT :{0}, :{1}, TagId FROM TagTable" - + " WHERE Tag = :{2}", - QueryType.CONNECT_BATCH_TAG.getParamName(0), - QueryType.CONNECT_BATCH_TAG.getParamName(1), - QueryType.CONNECT_BATCH_TAG.getParamName(2)); - - case GET_BATCH_TAGS: - return MessageFormat.format( - "SELECT Tag FROM TagTable INNER JOIN BatchTagTable ON TagTable.TagId = BatchTagTable.TagId" - + " WHERE SignerId = :{0} AND BatchId = :{1} ORDER BY Tag ASC", - QueryType.GET_BATCH_TAGS.getParamName(0), - QueryType.GET_BATCH_TAGS.getParamName(1)); - - case REMOVE_BATCH_TAGS: - return MessageFormat.format( - "DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.REMOVE_BATCH_TAGS.getParamName(0), - QueryType.REMOVE_BATCH_TAGS.getParamName(1)); - - default: - throw new IllegalArgumentException("Cannot serve a query of type " + queryType); - } - - } - - @Override - public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException { - - String serialString = Integer.toString(serialNum); - - switch(filterType) { - case EXACT_ENTRY: - return "MsgTable.EntryNum = :EntryNum" + serialString; - case MAX_ENTRY: - return "MsgTable.EntryNum <= :EntryNum" + serialString; - case MIN_ENTRY: - return "MsgTable.EntryNum >= :EntryNum" + serialString; - case MAX_MESSAGES: - return "LIMIT :Limit" + serialString; - case MSG_ID: - return "MsgTable.MsgId = :MsgId" + serialString; - case SIGNER_ID: - return "EXISTS (SELECT 1 FROM SignatureTable" - + " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)"; - case TAG: - return "EXISTS (SELECT 1 FROM TagTable" - + " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId" - + " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)"; - - case BEFORE_TIME: - return "MsgTable.ExactTime <= :TimeStamp"; - - case AFTER_TIME: - return "MsgTable.ExactTime >= :TimeStamp"; - - default: - throw new IllegalArgumentException("Cannot serve a filter of type " + filterType); - } - - } - - @Override - public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException { - - switch(filterType) { - case EXACT_ENTRY: // Go through - case MAX_ENTRY: // Go through - case MIN_ENTRY: // Go through - case MAX_MESSAGES: - return "INT"; - - case MSG_ID: // Go through - case SIGNER_ID: - return "TINYBLOB"; - - case TAG: - return "VARCHAR"; - - case AFTER_TIME: // Go through - case BEFORE_TIME: - return "TIMESTAMP"; - - default: - throw new IllegalArgumentException("Cannot serve a filter of type " + filterType); - } - - } - - @Override - public DataSource getDataSource() { - - BasicDataSource dataSource = new BasicDataSource(); - - dataSource.setDriverClassName("com.mysql.jdbc.Driver"); - dataSource.setUrl("jdbc:mysql://" + dbAddress + ":" + dbPort + "/" + dbName); - - dataSource.setUsername(username); - dataSource.setPassword(password); - - return dataSource; - - } - - @Override - public List getSchemaCreationCommands() { - List list = new LinkedList(); - - list.add("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INT NOT NULL AUTO_INCREMENT PRIMARY KEY," - + " MsgId TINYBLOB, ExactTime TIMESTAMP, Msg BLOB, UNIQUE(MsgId(50)))"); - - list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INT NOT NULL AUTO_INCREMENT PRIMARY KEY, Tag VARCHAR(50), UNIQUE(Tag))"); - - list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum INT, TagId INT," - + " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum)," - + " CONSTRAINT FOREIGN KEY (TagId) REFERENCES TagTable(TagId)," - + " CONSTRAINT UNIQUE (EntryNum, TagID))"); - - list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INT, SignerId TINYBLOB, Signature TINYBLOB," - + " INDEX(SignerId(32)), CONSTRAINT Unique_Signature UNIQUE(SignerId(32), EntryNum)," - + " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))"); - - list.add("CREATE TABLE IF NOT EXISTS BatchTable (SignerId TINYBLOB, BatchId INT, SerialNum INT, Data BLOB," - + " CONSTRAINT Unique_Batch UNIQUE(SignerId(32), BatchId, SerialNum))"); - - list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, TagId INT," - + " INDEX(SignerId(32), BatchId), CONSTRAINT FOREIGN KEY (TagId) REFERENCES TagTable(TagId))"); - - return list; - } - - @Override - public List getSchemaDeletionCommands() { - List list = new LinkedList(); - - list.add("DROP TABLE IF EXISTS BatchTagTable"); - list.add("DROP TABLE IF EXISTS BatchTable"); - list.add("DROP TABLE IF EXISTS MsgTagTable"); - list.add("DROP TABLE IF EXISTS SignatureTable"); - list.add("DROP TABLE IF EXISTS TagTable"); - list.add("DROP TABLE IF EXISTS MsgTable"); - - return list; - } -} +package meerkat.bulletinboard.sqlserver; + +import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider; +import meerkat.protobuf.BulletinBoardAPI.FilterType; +import org.apache.commons.dbcp2.BasicDataSource; + +import javax.sql.DataSource; +import java.text.MessageFormat; +import java.util.LinkedList; +import java.util.List; + +/** + * Created by Arbel Deutsch Peled on 09-Dec-15. + */ + +public class MySQLQueryProvider implements SQLQueryProvider { + + private String dbAddress; + private int dbPort; + private String dbName; + private String username; + private String password; + + public MySQLQueryProvider(String dbAddress, int dbPort, String dbName, String username, String password) { + this.dbAddress = dbAddress; + this.dbPort = dbPort; + this.dbName = dbName; + this.username = username; + this.password = password; + } + + @Override + public String getSQLString(QueryType queryType) throws IllegalArgumentException{ + + switch(queryType) { + + case ADD_SIGNATURE: + return MessageFormat.format( + "INSERT IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (:{0}, :{1}, :{2})", + QueryType.ADD_SIGNATURE.getParamName(0), + QueryType.ADD_SIGNATURE.getParamName(1), + QueryType.ADD_SIGNATURE.getParamName(2)); + + case CONNECT_TAG: + return MessageFormat.format( + "INSERT IGNORE INTO MsgTagTable (TagId, EntryNum)" + + " SELECT TagTable.TagId, :{0} AS EntryNum FROM TagTable WHERE Tag = :{1}", + QueryType.CONNECT_TAG.getParamName(0), + QueryType.CONNECT_TAG.getParamName(1)); + + case FIND_MSG_ID: + return MessageFormat.format( + "SELECT EntryNum From MsgTable WHERE MsgId = :{0}", + QueryType.FIND_MSG_ID.getParamName(0)); + + case FIND_TAG_ID: + return MessageFormat.format( + "SELECT TagId FROM TagTable WHERE Tag = :{0}", + QueryType.FIND_TAG_ID.getParamName(0)); + + case GET_MESSAGES: + return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"; + + case COUNT_MESSAGES: + return "SELECT COUNT(MsgTable.EntryNum) FROM MsgTable"; + + case GET_MESSAGE_STUBS: + return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable"; + + case GET_SIGNATURES: + return MessageFormat.format( + "SELECT Signature FROM SignatureTable WHERE EntryNum = :{0}", + QueryType.GET_SIGNATURES.getParamName(0)); + + case INSERT_MSG: + return MessageFormat.format( + "INSERT INTO MsgTable (MsgId, ExactTime, Msg) VALUES(:{0}, :{1}, :{2})", + QueryType.INSERT_MSG.getParamName(0), + QueryType.INSERT_MSG.getParamName(1), + QueryType.INSERT_MSG.getParamName(2)); + + case DELETE_MSG_BY_ENTRY: + return MessageFormat.format( + "DELETE IGNORE FROM MsgTable WHERE EntryNum = :{0}", + QueryType.DELETE_MSG_BY_ENTRY.getParamName(0)); + + case DELETE_MSG_BY_ID: + return MessageFormat.format( + "DELETE IGNORE FROM MsgTable WHERE MsgId = :{0}", + QueryType.DELETE_MSG_BY_ID.getParamName(0)); + + case INSERT_NEW_TAG: + return MessageFormat.format( + "INSERT IGNORE INTO TagTable(Tag) VALUES (:{0})", + QueryType.INSERT_NEW_TAG.getParamName(0)); + + case GET_LAST_MESSAGE_ENTRY: + return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable"; + + case GET_BATCH_MESSAGE_DATA_BY_MSG_ID: + return MessageFormat.format( + "SELECT Data FROM BatchTable" + + " INNER JOIN MsgTable ON MsgTable.EntryNum = BatchTable.EntryNum" + + " WHERE MsgTable.MsgId = :{0} AND BatchTable.SerialNum >= :{1}" + + " ORDER BY BatchTable.SerialNum ASC", + QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(0), + QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(1)); + + case GET_BATCH_MESSAGE_DATA_BY_BATCH_ID: + return MessageFormat.format( + "SELECT Data FROM BatchTable" + + " WHERE BatchId = :{0} AND SerialNum >= :{1}" + + " ORDER BY BatchTable.SerialNum ASC", + QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(0), + QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(1)); + + case INSERT_BATCH_DATA: + return MessageFormat.format( + "INSERT INTO BatchTable (BatchId, SerialNum, Data) VALUES (:{0}, :{1}, :{2})", + QueryType.INSERT_BATCH_DATA.getParamName(0), + QueryType.INSERT_BATCH_DATA.getParamName(1), + QueryType.INSERT_BATCH_DATA.getParamName(2)); + + case CHECK_BATCH_LENGTH: + return MessageFormat.format( + "SELECT COUNT(Data) AS BatchLength FROM BatchTable WHERE BatchId = :{0}", + QueryType.CHECK_BATCH_LENGTH.getParamName(0)); + + case CHECK_BATCH_OPEN: + return MessageFormat.format( + "SELECT COUNT(BatchId) AS batchCount FROM BatchTagTable WHERE BatchId = :{0}", + QueryType.CHECK_BATCH_OPEN.getParamName(0)); + + case STORE_BATCH_TAGS: + return MessageFormat.format( + "INSERT INTO BatchTagTable (Tags) VALUES (:{0})", + QueryType.STORE_BATCH_TAGS.getParamName(0)); + + case GET_BATCH_TAGS: + return MessageFormat.format( + "SELECT Tags FROM BatchTagTable WHERE BatchId = :{0}", + QueryType.GET_BATCH_TAGS.getParamName(0)); + + case ADD_ENTRY_NUM_TO_BATCH: + return MessageFormat.format( + "UPDATE BatchTable SET EntryNum = :{1} WHERE BatchId = :{0}", + QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(0), + QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1)); + + default: + throw new IllegalArgumentException("Cannot serve a query of type " + queryType); + } + + } + + @Override + public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException { + + String serialString = Integer.toString(serialNum); + + switch(filterType) { + case EXACT_ENTRY: + return "MsgTable.EntryNum = :EntryNum" + serialString; + case MAX_ENTRY: + return "MsgTable.EntryNum <= :EntryNum" + serialString; + case MIN_ENTRY: + return "MsgTable.EntryNum >= :EntryNum" + serialString; + case MAX_MESSAGES: + return "LIMIT :Limit" + serialString; + case MSG_ID: + return "MsgTable.MsgId = :MsgId" + serialString; + case SIGNER_ID: + return "EXISTS (SELECT 1 FROM SignatureTable" + + " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)"; + case TAG: + return "EXISTS (SELECT 1 FROM TagTable" + + " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId" + + " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)"; + + case BEFORE_TIME: + return "MsgTable.ExactTime <= :TimeStamp"; + + case AFTER_TIME: + return "MsgTable.ExactTime >= :TimeStamp"; + + default: + throw new IllegalArgumentException("Cannot serve a filter of type " + filterType); + } + + } + + @Override + public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException { + + switch(filterType) { + case EXACT_ENTRY: // Go through + case MAX_ENTRY: // Go through + case MIN_ENTRY: // Go through + case MAX_MESSAGES: + return "INT"; + + case MSG_ID: // Go through + case SIGNER_ID: + return "TINYBLOB"; + + case TAG: + return "VARCHAR"; + + case AFTER_TIME: // Go through + case BEFORE_TIME: + return "TIMESTAMP"; + + default: + throw new IllegalArgumentException("Cannot serve a filter of type " + filterType); + } + + } + + @Override + public DataSource getDataSource() { + + BasicDataSource dataSource = new BasicDataSource(); + + dataSource.setDriverClassName("com.mysql.jdbc.Driver"); + dataSource.setUrl("jdbc:mysql://" + dbAddress + ":" + dbPort + "/" + dbName); + + dataSource.setUsername(username); + dataSource.setPassword(password); + + return dataSource; + + } + + @Override + public List getSchemaCreationCommands() { + List list = new LinkedList(); + + list.add("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INT NOT NULL AUTO_INCREMENT PRIMARY KEY," + + " MsgId TINYBLOB, ExactTime TIMESTAMP, Msg BLOB, UNIQUE(MsgId(50)))"); + + list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INT NOT NULL AUTO_INCREMENT PRIMARY KEY, Tag VARCHAR(50), UNIQUE(Tag))"); + + list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum INT, TagId INT," + + " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE," + + " CONSTRAINT FOREIGN KEY (TagId) REFERENCES TagTable(TagId) ON DELETE CASCADE," + + " CONSTRAINT UNIQUE (EntryNum, TagID))"); + + list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INT, SignerId TINYBLOB, Signature TINYBLOB," + + " INDEX(SignerId(32)), CONSTRAINT Unique_Signature UNIQUE(SignerId(32), EntryNum)," + + " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE)"); + + list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (BatchId INT AUTO_INCREMENT PRIMARY KEY, Tags BLOB)"); + + list.add("CREATE TABLE IF NOT EXISTS BatchTable (BatchId INT, EntryNum INT, SerialNum INT, Data BLOB," + + " CONSTRAINT UNIQUE(BatchId, SerialNum)," + + " CONSTRAINT FOREIGN KEY (BatchId) REFERENCES BatchTagTable(BatchId) ON DELETE CASCADE)"); + + + + return list; + } + + @Override + public List getSchemaDeletionCommands() { + List list = new LinkedList(); + + list.add("DROP TABLE IF EXISTS BatchTable"); + list.add("DROP TABLE IF EXISTS BatchTagTable"); + list.add("DROP TABLE IF EXISTS MsgTagTable"); + list.add("DROP TABLE IF EXISTS SignatureTable"); + list.add("DROP TABLE IF EXISTS TagTable"); + list.add("DROP TABLE IF EXISTS MsgTable"); + + return list; + } +} \ No newline at end of file diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/SQLiteQueryProvider.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/SQLiteQueryProvider.java index 00f75a0..9f68955 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/SQLiteQueryProvider.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/SQLiteQueryProvider.java @@ -1,233 +1,237 @@ -package meerkat.bulletinboard.sqlserver; - -import meerkat.protobuf.BulletinBoardAPI.*; -import org.sqlite.SQLiteDataSource; - -import javax.sql.DataSource; -import java.text.MessageFormat; -import java.util.LinkedList; -import java.util.List; - -/** - * Created by Arbel Deutsch Peled on 09-Dec-15. - */ - -public class SQLiteQueryProvider implements BulletinBoardSQLServer.SQLQueryProvider { - - String dbName; - - public SQLiteQueryProvider(String dbName) { - this.dbName = dbName; - } - - @Override - public String getSQLString(QueryType queryType) throws IllegalArgumentException{ - - switch(queryType) { - case ADD_SIGNATURE: - return "INSERT OR IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (:EntryNum,:SignerId,:Signature)"; - - case CONNECT_TAG: - return "INSERT OR IGNORE INTO MsgTagTable (TagId, EntryNum)" - + " SELECT TagTable.TagId, :EntryNum AS EntryNum FROM TagTable WHERE Tag = :Tag"; - - case FIND_MSG_ID: - return "SELECT EntryNum From MsgTable WHERE MsgId = :MsgId"; - - case FIND_TAG_ID: - return MessageFormat.format( - "SELECT TagId FROM TagTable WHERE Tag = :{0}", - QueryType.FIND_TAG_ID.getParamName(0)); - - case GET_MESSAGES: - return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"; - - case COUNT_MESSAGES: - return "SELECT COUNT(MsgTable.EntryNum) FROM MsgTable"; - - case GET_MESSAGE_STUBS: - return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable"; - - case GET_SIGNATURES: - return "SELECT Signature FROM SignatureTable WHERE EntryNum = :EntryNum"; - - case INSERT_MSG: - return "INSERT INTO MsgTable (MsgId, Msg) VALUES(:MsgId,:Msg)"; - - case INSERT_NEW_TAG: - return "INSERT OR IGNORE INTO TagTable(Tag) VALUES (:Tag)"; - - case GET_LAST_MESSAGE_ENTRY: - return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable"; - - case GET_BATCH_MESSAGE_ENTRY: - return MessageFormat.format( - "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable" - + " INNER JOIN SignatureTable ON MsgTable.EntryNum = SignatureTable.EntryNum" - + " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum" - + " INNER JOIN TagTable ON MsgTagTable.TagId = TagTable.TagId" - + " WHERE SignatureTable.SignerId = :{0}" - + " AND TagTable.Tag = :{1}", - QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0), - QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1)); - - case GET_BATCH_MESSAGE_DATA: - return MessageFormat.format( - "SELECT Data FROM BatchTable" - + " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}" - + " ORDER BY SerialNum ASC", - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0), - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1), - QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2)); - - case INSERT_BATCH_DATA: - return MessageFormat.format( - "INSERT INTO BatchTable (SignerId, BatchId, SerialNum, Data)" - + " VALUES (:{0}, :{1}, :{2}, :{3})", - QueryType.INSERT_BATCH_DATA.getParamName(0), - QueryType.INSERT_BATCH_DATA.getParamName(1), - QueryType.INSERT_BATCH_DATA.getParamName(2), - QueryType.INSERT_BATCH_DATA.getParamName(3)); - - case CHECK_BATCH_LENGTH: - return MessageFormat.format( - "SELECT COUNT(Data) AS BatchLength FROM BatchTable" - + " WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.CHECK_BATCH_LENGTH.getParamName(0), - QueryType.CHECK_BATCH_LENGTH.getParamName(1)); - - case CONNECT_BATCH_TAG: - return MessageFormat.format( - "INSERT INTO BatchTagTable (SignerId, BatchId, TagId) SELECT :{0}, :{1}, TagId FROM TagTable" - + " WHERE Tag = :{2}", - QueryType.CONNECT_BATCH_TAG.getParamName(0), - QueryType.CONNECT_BATCH_TAG.getParamName(1), - QueryType.CONNECT_BATCH_TAG.getParamName(2)); - - case GET_BATCH_TAGS: - return MessageFormat.format( - "SELECT Tag FROM TagTable INNER JOIN BatchTagTable ON TagTable.TagId = BatchTagTable.TagId" - + " WHERE SignerId = :{0} AND BatchId = :{1} ORDER BY Tag ASC", - QueryType.GET_BATCH_TAGS.getParamName(0), - QueryType.GET_BATCH_TAGS.getParamName(1)); - - case REMOVE_BATCH_TAGS: - return MessageFormat.format( - "DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}", - QueryType.REMOVE_BATCH_TAGS.getParamName(0), - QueryType.REMOVE_BATCH_TAGS.getParamName(1)); - - default: - throw new IllegalArgumentException("Cannot serve a query of type " + queryType); - } - - } - - @Override - public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException { - - String serialString = Integer.toString(serialNum); - - switch(filterType) { - case EXACT_ENTRY: - return "MsgTable.EntryNum = :EntryNum" + serialString; - - case MAX_ENTRY: - return "MsgTable.EntryNum <= :EntryNum" + serialString; - - case MIN_ENTRY: - return "MsgTable.EntryNum <= :EntryNum" + serialString; - - case MAX_MESSAGES: - return "LIMIT = :Limit" + serialString; - - case MSG_ID: - return "MsgTable.MsgId = :MsgId" + serialString; - - case SIGNER_ID: - return "EXISTS (SELECT 1 FROM SignatureTable" - + " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)"; - - case TAG: - return "EXISTS (SELECT 1 FROM TagTable" - + " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId" - + " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)"; - - case BEFORE_TIME: - return "MsgTable.ExactTime <= :TimeStamp"; - - case AFTER_TIME: - return "MsgTable.ExactTime >= :TimeStamp"; - - default: - throw new IllegalArgumentException("Cannot serve a filter of type " + filterType); - } - - } - - @Override - public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException { - - switch(filterType) { - case EXACT_ENTRY: // Go through - case MAX_ENTRY: // Go through - case MIN_ENTRY: // Go through - case MAX_MESSAGES: - return "INTEGER"; - - case MSG_ID: // Go through - case SIGNER_ID: - return "BLOB"; - - case TAG: - return "VARCHAR"; - - default: - throw new IllegalArgumentException("Cannot serve a filter of type " + filterType); - } - - } - - @Override - public DataSource getDataSource() { - - SQLiteDataSource dataSource = new SQLiteDataSource(); - dataSource.setUrl("jdbc:sqlite:" + dbName); - - return dataSource; - } - - - @Override - public List getSchemaCreationCommands() { - List list = new LinkedList(); - - list.add("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INTEGER PRIMARY KEY, MsgId BLOB UNIQUE, Msg BLOB)"); - - list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INTEGER PRIMARY KEY, Tag varchar(50) UNIQUE)"); - list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum BLOB, TagId INTEGER, FOREIGN KEY (EntryNum)" - + " REFERENCES MsgTable(EntryNum), FOREIGN KEY (TagId) REFERENCES TagTable(TagId), UNIQUE (EntryNum, TagID))"); - - list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INTEGER, SignerId BLOB, Signature BLOB," - + " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum), UNIQUE(SignerId, EntryNum))"); - - list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)"); - - return list; - } - - @Override - public List getSchemaDeletionCommands() { - List list = new LinkedList(); - - list.add("DROP TABLE IF EXISTS MsgTagTable"); - - list.add("DROP INDEX IF EXISTS SignerIndex"); - list.add("DROP TABLE IF EXISTS SignatureTable"); - - list.add("DROP TABLE IF EXISTS TagTable"); - list.add("DROP TABLE IF EXISTS MsgTable"); - - return list; - } -} +package meerkat.bulletinboard.sqlserver; + +import meerkat.protobuf.BulletinBoardAPI.*; +import org.apache.commons.dbcp2.BasicDataSource; +import org.sqlite.SQLiteDataSource; + +import javax.sql.DataSource; +import java.text.MessageFormat; +import java.util.LinkedList; +import java.util.List; + +/** + * Created by Arbel Deutsch Peled on 09-Dec-15. + */ + +public class SQLiteQueryProvider implements BulletinBoardSQLServer.SQLQueryProvider { + + String dbName; + + public SQLiteQueryProvider(String dbName) { + this.dbName = dbName; + } + + @Override + public String getSQLString(QueryType queryType) throws IllegalArgumentException{ + + switch(queryType) { + case ADD_SIGNATURE: + return "INSERT OR IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (:EntryNum,:SignerId,:Signature)"; + + case CONNECT_TAG: + return "INSERT OR IGNORE INTO MsgTagTable (TagId, EntryNum)" + + " SELECT TagTable.TagId, :EntryNum AS EntryNum FROM TagTable WHERE Tag = :Tag"; + + case FIND_MSG_ID: + return "SELECT EntryNum From MsgTable WHERE MsgId = :MsgId"; + + case FIND_TAG_ID: + return MessageFormat.format( + "SELECT TagId FROM TagTable WHERE Tag = :{0}", + QueryType.FIND_TAG_ID.getParamName(0)); + + case GET_MESSAGES: + return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"; + + case COUNT_MESSAGES: + return "SELECT COUNT(MsgTable.EntryNum) FROM MsgTable"; + + case GET_MESSAGE_STUBS: + return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable"; + + case GET_SIGNATURES: + return "SELECT Signature FROM SignatureTable WHERE EntryNum = :EntryNum"; + + case INSERT_MSG: + return "INSERT INTO MsgTable (MsgId, Msg) VALUES(:MsgId,:Msg)"; + + case INSERT_NEW_TAG: + return "INSERT OR IGNORE INTO TagTable(Tag) VALUES (:Tag)"; + + case GET_LAST_MESSAGE_ENTRY: + return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable"; + + case GET_BATCH_MESSAGE_DATA_BY_MSG_ID: + return MessageFormat.format( + "SELECT Data FROM BatchTable" + + " INNER JOIN MsgTable ON MsgTable.EntryNum = BatchTable.EntryNum" + + " WHERE MsgTable.MsgId = :{0} AND BatchTable.SerialNum >= :{1}" + + " ORDER BY BatchTable.SerialNum ASC", + QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(0), + QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(1)); + + case GET_BATCH_MESSAGE_DATA_BY_BATCH_ID: + return MessageFormat.format( + "SELECT Data FROM BatchTable" + + " WHERE BatchId = :{0} AND SerialNum >= :{1}" + + " ORDER BY BatchTable.SerialNum ASC", + QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(0), + QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(1)); + + case INSERT_BATCH_DATA: + return MessageFormat.format( + "INSERT INTO BatchTable (BatchId, SerialNum, Data) VALUES (:{0}, :{1}, :{2})", + QueryType.INSERT_BATCH_DATA.getParamName(0), + QueryType.INSERT_BATCH_DATA.getParamName(1), + QueryType.INSERT_BATCH_DATA.getParamName(2)); + + case CHECK_BATCH_LENGTH: + return MessageFormat.format( + "SELECT COUNT(Data) AS BatchLength FROM BatchTable WHERE BatchId = :{0}", + QueryType.CHECK_BATCH_LENGTH.getParamName(0)); + + case CHECK_BATCH_OPEN: + return MessageFormat.format( + "SELECT COUNT(BatchId) AS batchCount FROM BatchTagTable WHERE BatchId = :{0}", + QueryType.CHECK_BATCH_OPEN.getParamName(0)); + + case STORE_BATCH_TAGS: + return MessageFormat.format( + "INSERT INTO BatchTagTable (Tags) VALUES (:{0})", + QueryType.STORE_BATCH_TAGS.getParamName(0)); + + case GET_BATCH_TAGS: + return MessageFormat.format( + "SELECT Tags FROM BatchTagTable WHERE BatchId = :{0}", + QueryType.GET_BATCH_TAGS.getParamName(0)); + + case ADD_ENTRY_NUM_TO_BATCH: + return MessageFormat.format( + "UPDATE BatchTable SET EntryNum = :{1} WHERE BatchId = :{0}", + QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(0), + QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1)); + + default: + throw new IllegalArgumentException("Cannot serve a query of type " + queryType); + } + + } + + @Override + public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException { + + String serialString = Integer.toString(serialNum); + + switch(filterType) { + case EXACT_ENTRY: + return "MsgTable.EntryNum = :EntryNum" + serialString; + + case MAX_ENTRY: + return "MsgTable.EntryNum <= :EntryNum" + serialString; + + case MIN_ENTRY: + return "MsgTable.EntryNum <= :EntryNum" + serialString; + + case MAX_MESSAGES: + return "LIMIT = :Limit" + serialString; + + case MSG_ID: + return "MsgTable.MsgId = :MsgId" + serialString; + + case SIGNER_ID: + return "EXISTS (SELECT 1 FROM SignatureTable" + + " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)"; + + case TAG: + return "EXISTS (SELECT 1 FROM TagTable" + + " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId" + + " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)"; + + case BEFORE_TIME: + return "MsgTable.ExactTime <= :TimeStamp"; + + case AFTER_TIME: + return "MsgTable.ExactTime >= :TimeStamp"; + + default: + throw new IllegalArgumentException("Cannot serve a filter of type " + filterType); + } + + } + + @Override + public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException { + + switch(filterType) { + case EXACT_ENTRY: // Go through + case MAX_ENTRY: // Go through + case MIN_ENTRY: // Go through + case MAX_MESSAGES: + return "INTEGER"; + + case MSG_ID: // Go through + case SIGNER_ID: + return "BLOB"; + + case TAG: + return "VARCHAR"; + + default: + throw new IllegalArgumentException("Cannot serve a filter of type " + filterType); + } + + } + + @Override + public DataSource getDataSource() { + + BasicDataSource dataSource = new BasicDataSource(); + dataSource.setDriverClassName("org.sqlite.JDBC"); + dataSource.setUrl("jdbc:sqlite:" + dbName); + + return dataSource; + } + + + @Override + public List getSchemaCreationCommands() { + List list = new LinkedList(); + + list.add("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INTEGER PRIMARY KEY, MsgId BLOB UNIQUE, Msg BLOB)"); + + list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INTEGER PRIMARY KEY, Tag varchar(50) UNIQUE)"); + list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum BLOB, TagId INTEGER," + + " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE," + + " FOREIGN KEY (TagId) REFERENCES TagTable(TagId) ON DELETE CASCADE," + + " UNIQUE (EntryNum, TagID))"); + + list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INTEGER, SignerId BLOB, Signature BLOB," + + " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE," + + " UNIQUE(SignerId, EntryNum))"); + + list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)"); + + list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (BatchId INTEGER PRIMARY KEY, Tags BLOB)"); + + list.add("CREATE TABLE IF NOT EXISTS BatchTable (BatchId INTEGER, EntryNum INTEGER, SerialNum INTEGER, Data BLOB," + + " UNIQUE(BatchId, SerialNum)," + + " FOREIGN KEY (BatchId) REFERENCES BatchTagTable(BatchId) ON DELETE CASCADE)"); + + return list; + } + + @Override + public List getSchemaDeletionCommands() { + List list = new LinkedList(); + + list.add("DROP TABLE IF EXISTS MsgTagTable"); + + list.add("DROP INDEX IF EXISTS SignerIndex"); + list.add("DROP TABLE IF EXISTS SignatureTable"); + + list.add("DROP TABLE IF EXISTS TagTable"); + list.add("DROP TABLE IF EXISTS MsgTable"); + + return list; + } +} diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BatchDataCallbackHandler.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BatchDataCallbackHandler.java index 9ad0dc7..69d7bae 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BatchDataCallbackHandler.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BatchDataCallbackHandler.java @@ -1,10 +1,8 @@ package meerkat.bulletinboard.sqlserver.mappers; -import com.google.protobuf.InvalidProtocolBufferException; import meerkat.comm.MessageOutputStream; -import meerkat.protobuf.BulletinBoardAPI.BatchData; +import meerkat.protobuf.BulletinBoardAPI.BatchChunk; import org.springframework.jdbc.core.RowCallbackHandler; -import org.springframework.jdbc.core.RowMapper; import java.io.IOException; import java.sql.ResultSet; @@ -15,16 +13,16 @@ import java.sql.SQLException; */ public class BatchDataCallbackHandler implements RowCallbackHandler { - private final MessageOutputStream out; + private final MessageOutputStream out; - public BatchDataCallbackHandler(MessageOutputStream out) { + public BatchDataCallbackHandler(MessageOutputStream out) { this.out = out; } @Override public void processRow(ResultSet rs) throws SQLException { try { - out.writeMessage(BatchData.parseFrom(rs.getBytes(1))); + out.writeMessage(BatchChunk.parseFrom(rs.getBytes(1))); } catch (IOException e) { //TODO: Log } diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BatchDataDigestHandler.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BatchDataDigestHandler.java new file mode 100644 index 0000000..ae55d5c --- /dev/null +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BatchDataDigestHandler.java @@ -0,0 +1,31 @@ +package meerkat.bulletinboard.sqlserver.mappers; + +import meerkat.bulletinboard.BulletinBoardDigest; +import meerkat.protobuf.BulletinBoardAPI.BatchChunk; +import org.springframework.jdbc.core.RowCallbackHandler; + +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Created by Arbel Deutsch Peled on 19-Dec-15. + */ +public class BatchDataDigestHandler implements RowCallbackHandler { + + private final BulletinBoardDigest digest; + + public BatchDataDigestHandler(BulletinBoardDigest digest) { + this.digest = digest; + } + + @Override + public void processRow(ResultSet rs) throws SQLException { + try { + BatchChunk batchChunk = BatchChunk.parseFrom(rs.getBytes(1)); + digest.update(batchChunk.getData().toByteArray()); + } catch (IOException e) { + //TODO: Log + } + } +} diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BatchDataMapper.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BatchDataMapper.java deleted file mode 100644 index bc4ea26..0000000 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BatchDataMapper.java +++ /dev/null @@ -1,26 +0,0 @@ -package meerkat.bulletinboard.sqlserver.mappers; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import meerkat.protobuf.BulletinBoardAPI.BatchData; -import org.springframework.jdbc.core.RowMapper; - -import java.sql.ResultSet; -import java.sql.SQLException; - -/** - * Created by Arbel Deutsch Peled on 19-Dec-15. - */ -public class BatchDataMapper implements RowMapper { - - @Override - public BatchData mapRow(ResultSet rs, int rowNum) throws SQLException { - - try { - return BatchData.parseFrom(rs.getBytes(1)); - } catch (InvalidProtocolBufferException e) { - return BatchData.getDefaultInstance(); - } - - } -} diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BeginBatchMessageMapper.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BeginBatchMessageMapper.java new file mode 100644 index 0000000..0f72ec9 --- /dev/null +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BeginBatchMessageMapper.java @@ -0,0 +1,24 @@ +package meerkat.bulletinboard.sqlserver.mappers; + +import com.google.protobuf.InvalidProtocolBufferException; +import meerkat.protobuf.BulletinBoardAPI.BeginBatchMessage; +import org.springframework.jdbc.core.RowMapper; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Created by Arbel Deutsch Peled on 20-Dec-15. + */ +public class BeginBatchMessageMapper implements RowMapper { + + @Override + public BeginBatchMessage mapRow(ResultSet rs, int rowNum) throws SQLException { + try { + return BeginBatchMessage.newBuilder().mergeFrom(rs.getBytes(1)).build(); + } catch (InvalidProtocolBufferException e) { + return null; + } + } + +} diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/LongMapper.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/LongMapper.java index ba5a378..1ec0d98 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/LongMapper.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/LongMapper.java @@ -1,18 +1,18 @@ -package meerkat.bulletinboard.sqlserver.mappers; - -import meerkat.protobuf.BulletinBoardAPI.MessageID; -import org.springframework.jdbc.core.RowMapper; - -import java.sql.ResultSet; -import java.sql.SQLException; - -/** - * Created by Arbel Deutsch Peled on 11-Dec-15. - */ -public class LongMapper implements RowMapper { - - @Override - public Long mapRow(ResultSet rs, int rowNum) throws SQLException { - return rs.getLong(1); - } -} +package meerkat.bulletinboard.sqlserver.mappers; + +import meerkat.protobuf.BulletinBoardAPI.MessageID; +import org.springframework.jdbc.core.RowMapper; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Created by Arbel Deutsch Peled on 11-Dec-15. + */ +public class LongMapper implements RowMapper { + + @Override + public Long mapRow(ResultSet rs, int rowNum) throws SQLException { + return rs.getLong(1); + } +} diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/MessageMapper.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/MessageMapper.java index d1584aa..fdc1fa8 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/MessageMapper.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/MessageMapper.java @@ -1,32 +1,32 @@ -package meerkat.bulletinboard.sqlserver.mappers; - -import com.google.protobuf.InvalidProtocolBufferException; -import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage; -import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage; -import org.springframework.jdbc.core.RowMapper; - -import java.sql.ResultSet; -import java.sql.SQLException; - -/** - * Created by Arbel Deutsch Peled on 11-Dec-15. - */ -public class MessageMapper implements RowMapper { - - @Override - public BulletinBoardMessage.Builder mapRow(ResultSet rs, int rowNum) throws SQLException { - - BulletinBoardMessage.Builder builder = BulletinBoardMessage.newBuilder(); - - try { - builder.setEntryNum(rs.getLong(1)) - .setMsg(UnsignedBulletinBoardMessage.parseFrom(rs.getBytes(2))); - - } catch (InvalidProtocolBufferException e) { - throw new SQLException(e.getMessage(), e); - } - - return builder; - } - -} +package meerkat.bulletinboard.sqlserver.mappers; + +import com.google.protobuf.InvalidProtocolBufferException; +import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage; +import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage; +import org.springframework.jdbc.core.RowMapper; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Created by Arbel Deutsch Peled on 11-Dec-15. + */ +public class MessageMapper implements RowMapper { + + @Override + public BulletinBoardMessage.Builder mapRow(ResultSet rs, int rowNum) throws SQLException { + + BulletinBoardMessage.Builder builder = BulletinBoardMessage.newBuilder(); + + try { + builder.setEntryNum(rs.getLong(1)) + .setMsg(UnsignedBulletinBoardMessage.parseFrom(rs.getBytes(2))); + + } catch (InvalidProtocolBufferException e) { + throw new SQLException(e.getMessage(), e); + } + + return builder; + } + +} diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/MessageStubMapper.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/MessageStubMapper.java index 1f9c459..e9174e5 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/MessageStubMapper.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/MessageStubMapper.java @@ -21,7 +21,7 @@ public class MessageStubMapper implements RowMapper { return BulletinBoardMessage.newBuilder() .setEntryNum(rs.getLong(1)) .setMsg(UnsignedBulletinBoardMessage.newBuilder() - .setData(ByteString.copyFrom(rs.getBytes(2))) + .setMsgId(ByteString.copyFrom(rs.getBytes(2))) .setTimestamp(BulletinBoardUtils.toTimestampProto(rs.getTimestamp(3))) .build()) .build(); diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/SignatureMapper.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/SignatureMapper.java index 95ff087..60015c1 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/SignatureMapper.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/SignatureMapper.java @@ -1,28 +1,28 @@ -package meerkat.bulletinboard.sqlserver.mappers; - -import com.google.protobuf.InvalidProtocolBufferException; -import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage; -import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage; -import meerkat.protobuf.Crypto.Signature; -import org.springframework.jdbc.core.RowMapper; - -import java.sql.ResultSet; -import java.sql.SQLException; - -/** - * Created by Arbel Deutsch Peled on 11-Dec-15. - */ -public class SignatureMapper implements RowMapper { - - @Override - public Signature mapRow(ResultSet rs, int rowNum) throws SQLException { - - try { - return Signature.parseFrom(rs.getBytes(1)); - } catch (InvalidProtocolBufferException e) { - throw new SQLException(e.getMessage(), e); - } - - } - -} +package meerkat.bulletinboard.sqlserver.mappers; + +import com.google.protobuf.InvalidProtocolBufferException; +import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage; +import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage; +import meerkat.protobuf.Crypto.Signature; +import org.springframework.jdbc.core.RowMapper; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Created by Arbel Deutsch Peled on 11-Dec-15. + */ +public class SignatureMapper implements RowMapper { + + @Override + public Signature mapRow(ResultSet rs, int rowNum) throws SQLException { + + try { + return Signature.parseFrom(rs.getBytes(1)); + } catch (InvalidProtocolBufferException e) { + throw new SQLException(e.getMessage(), e); + } + + } + +} diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java index aef820e..8e9d161 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java @@ -1,266 +1,268 @@ -package meerkat.bulletinboard.webapp; - -import javax.servlet.ServletContext; -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; -import javax.ws.rs.*; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.StreamingOutput; - -import com.google.protobuf.BoolValue; -import meerkat.bulletinboard.BulletinBoardServer; -import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; -import meerkat.bulletinboard.sqlserver.H2QueryProvider; -import meerkat.bulletinboard.sqlserver.MySQLQueryProvider; -import meerkat.bulletinboard.sqlserver.SQLiteQueryProvider; -import meerkat.comm.CommunicationException; -import meerkat.comm.MessageOutputStream; -import meerkat.protobuf.BulletinBoardAPI.*; -import meerkat.protobuf.Comm.*; -import static meerkat.bulletinboard.BulletinBoardConstants.*; -import static meerkat.rest.Constants.*; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.Collection; - -/** - * An implementation of the BulletinBoardServer which functions as a WebApp - */ -@Path(BULLETIN_BOARD_SERVER_PATH) -public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextListener{ - - private static final String BULLETIN_BOARD_ATTRIBUTE_NAME = "bulletinBoard"; - - @Context ServletContext servletContext; - - BulletinBoardServer bulletinBoard; - - /** - * This is the servlet init method. - */ - public void init(){ - bulletinBoard = (BulletinBoardServer) servletContext.getAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME); - } - - /** - * This is the BulletinBoard init method. - */ - @Override - public void init(String meerkatDB) throws CommunicationException { - bulletinBoard.init(meerkatDB); - } - - @Override - public void contextInitialized(ServletContextEvent servletContextEvent) { - ServletContext servletContext = servletContextEvent.getServletContext(); - String dbType = servletContext.getInitParameter("dbType"); - String dbName = servletContext.getInitParameter("dbName"); - - if ("SQLite".equals(dbType)){ - - bulletinBoard = new BulletinBoardSQLServer(new SQLiteQueryProvider(dbName)); - - } else if ("H2".equals(dbType)) { - - bulletinBoard = new BulletinBoardSQLServer(new H2QueryProvider(dbName)); - - } else if ("MySQL".equals(dbType)) { - - String dbAddress = servletContext.getInitParameter("dbAddress"); - int dbPort = Integer.parseInt(servletContext.getInitParameter("dbPort")); - String username = servletContext.getInitParameter("username"); - String password = servletContext.getInitParameter("password"); - - bulletinBoard = new BulletinBoardSQLServer(new MySQLQueryProvider(dbAddress,dbPort,dbName,username,password)); - } - - try { - init(dbName); - servletContext.setAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME, bulletinBoard); - } catch (CommunicationException e) { - System.err.println(e.getMessage()); - } - } - - @Path(POST_MESSAGE_PATH) - @POST - @Consumes(MEDIATYPE_PROTOBUF) - @Produces(MEDIATYPE_PROTOBUF) - @Override - public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException { - init(); - return bulletinBoard.postMessage(msg); - } - - @Override - public void readMessages(MessageFilterList filterList, MessageOutputStream out) throws CommunicationException { - init(); - bulletinBoard.readMessages(filterList, out); - } - - - @Path(READ_MESSAGES_PATH) - @POST - @Consumes(MEDIATYPE_PROTOBUF) - /** - * Wrapper for the readMessages method which streams the output into the response - */ - public StreamingOutput readMessages(final MessageFilterList filterList) { - - return new StreamingOutput() { - - @Override - public void write(OutputStream output) throws IOException, WebApplicationException { - MessageOutputStream out = new MessageOutputStream<>(output); - - try { - init(); - bulletinBoard.readMessages(filterList, out); - } catch (CommunicationException e) { - //TODO: Log - out.writeMessage(null); - } - } - - }; - - } - - @Path(BEGIN_BATCH_PATH) - @POST - @Consumes(MEDIATYPE_PROTOBUF) - @Produces(MEDIATYPE_PROTOBUF) - @Override - public BoolValue beginBatch(BeginBatchMessage message) { - try { - init(); - return bulletinBoard.beginBatch(message); - } catch (CommunicationException e) { - System.err.println(e.getMessage()); - return null; - } - } - - @Path(POST_BATCH_PATH) - @POST - @Consumes(MEDIATYPE_PROTOBUF) - @Produces(MEDIATYPE_PROTOBUF) - @Override - public BoolValue postBatchMessage(BatchMessage batchMessage) { - try { - init(); - return bulletinBoard.postBatchMessage(batchMessage); - } catch (CommunicationException e) { - System.err.println(e.getMessage()); - return null; - } - } - - @Path(CLOSE_BATCH_PATH) - @POST - @Consumes(MEDIATYPE_PROTOBUF) - @Produces(MEDIATYPE_PROTOBUF) - @Override - public BoolValue closeBatchMessage(CloseBatchMessage message) { - try { - init(); - return bulletinBoard.closeBatchMessage(message); - } catch (CommunicationException e) { - System.err.println(e.getMessage()); - return null; - } - } - - - @Override - public void readBatch(BatchSpecificationMessage message, MessageOutputStream out) { - try { - init(); - bulletinBoard.readBatch(message, out); - } catch (CommunicationException | IllegalArgumentException e) { - System.err.println(e.getMessage()); - } - } - - @Path(GENERATE_SYNC_QUERY_PATH) - @POST - @Consumes(MEDIATYPE_PROTOBUF) - @Produces(MEDIATYPE_PROTOBUF) - @Override - public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { - try { - init(); - return bulletinBoard.generateSyncQuery(generateSyncQueryParams); - } catch (CommunicationException | IllegalArgumentException e) { - System.err.println(e.getMessage()); - return null; - } - } - - @Path(READ_BATCH_PATH) - @POST - @Consumes(MEDIATYPE_PROTOBUF) - /** - * Wrapper for the readBatch method which streams the output into the response - */ - public StreamingOutput readBatch(final BatchSpecificationMessage message) { - - return new StreamingOutput() { - - @Override - public void write(OutputStream output) throws IOException, WebApplicationException { - MessageOutputStream out = new MessageOutputStream<>(output); - - try { - init(); - bulletinBoard.readBatch(message, out); - } catch (CommunicationException e) { - //TODO: Log - out.writeMessage(null); - } - } - - }; - - } - - @Path(SYNC_QUERY_PATH) - @POST - @Consumes(MEDIATYPE_PROTOBUF) - @Produces(MEDIATYPE_PROTOBUF) - @Override - public SyncQueryResponse querySync(SyncQuery syncQuery) throws CommunicationException { - try{ - init(); - return bulletinBoard.querySync(syncQuery); - } catch (CommunicationException | IllegalArgumentException e) { - System.err.println(e.getMessage()); - return null; - } - } - - @Override - public void close(){ - try { - bulletinBoard.close(); - } catch (CommunicationException e) { - System.err.println(e.getMessage()); - } - } - - @GET - @Produces(MediaType.TEXT_PLAIN) - public String test() { - return "This BulletinBoard is up and running!\n Please consult the API documents to perform queries."; - } - - @Override - public void contextDestroyed(ServletContextEvent servletContextEvent) { - ServletContext servletContext = servletContextEvent.getServletContext(); - bulletinBoard = (BulletinBoardServer) servletContext.getAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME); - close(); - } - -} +package meerkat.bulletinboard.webapp; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.ws.rs.*; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.StreamingOutput; + +import com.google.protobuf.BoolValue; +import com.google.protobuf.Int32Value; +import com.google.protobuf.Int64Value; +import meerkat.bulletinboard.BulletinBoardServer; +import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; +import meerkat.bulletinboard.sqlserver.H2QueryProvider; +import meerkat.bulletinboard.sqlserver.MySQLQueryProvider; +import meerkat.bulletinboard.sqlserver.SQLiteQueryProvider; +import meerkat.comm.CommunicationException; +import meerkat.comm.MessageOutputStream; +import meerkat.protobuf.BulletinBoardAPI.*; +import static meerkat.bulletinboard.BulletinBoardConstants.*; +import static meerkat.rest.Constants.*; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * An implementation of the BulletinBoardServer which functions as a WebApp + */ +@Path(BULLETIN_BOARD_SERVER_PATH) +public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextListener{ + + private static final String BULLETIN_BOARD_ATTRIBUTE_NAME = "bulletinBoard"; + + @Context ServletContext servletContext; + + BulletinBoardServer bulletinBoard; + + /** + * This is the servlet init method. + */ + public void init(){ + bulletinBoard = (BulletinBoardServer) servletContext.getAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME); + } + + @Override + public void contextInitialized(ServletContextEvent servletContextEvent) { + ServletContext servletContext = servletContextEvent.getServletContext(); + String dbType = servletContext.getInitParameter("dbType"); + String dbName = servletContext.getInitParameter("dbName"); + + if ("SQLite".equals(dbType)){ + + bulletinBoard = new BulletinBoardSQLServer(new SQLiteQueryProvider(dbName)); + + } else if ("H2".equals(dbType)) { + + bulletinBoard = new BulletinBoardSQLServer(new H2QueryProvider(dbName)); + + } else if ("MySQL".equals(dbType)) { + + String dbAddress = servletContext.getInitParameter("dbAddress"); + int dbPort = Integer.parseInt(servletContext.getInitParameter("dbPort")); + String username = servletContext.getInitParameter("username"); + String password = servletContext.getInitParameter("password"); + + bulletinBoard = new BulletinBoardSQLServer(new MySQLQueryProvider(dbAddress,dbPort,dbName,username,password)); + } + + try { + bulletinBoard.init(); + servletContext.setAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME, bulletinBoard); + } catch (CommunicationException e) { + System.err.println(e.getMessage()); + } + } + + @Path(POST_MESSAGE_PATH) + @POST + @Consumes(MEDIATYPE_PROTOBUF) + @Produces(MEDIATYPE_PROTOBUF) + @Override + public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException { + init(); + return bulletinBoard.postMessage(msg); + } + + @Override + public void readMessages(MessageFilterList filterList, MessageOutputStream out) throws CommunicationException { + init(); + bulletinBoard.readMessages(filterList, out); + } + + @Path(COUNT_MESSAGES_PATH) + @POST + @Consumes(MEDIATYPE_PROTOBUF) + @Produces(MEDIATYPE_PROTOBUF) + @Override + public Int32Value getMessageCount(MessageFilterList filterList) throws CommunicationException { + init(); + return bulletinBoard.getMessageCount(filterList); + } + + + @Path(READ_MESSAGES_PATH) + @POST + @Consumes(MEDIATYPE_PROTOBUF) + /** + * Wrapper for the readMessages method which streams the output into the response + */ + public StreamingOutput readMessages(final MessageFilterList filterList) { + + return new StreamingOutput() { + + @Override + public void write(OutputStream output) throws IOException, WebApplicationException { + MessageOutputStream out = new MessageOutputStream<>(output); + + try { + init(); + bulletinBoard.readMessages(filterList, out); + } catch (CommunicationException e) { + //TODO: Log + out.writeMessage(null); + } + } + + }; + + } + + @Path(BEGIN_BATCH_PATH) + @POST + @Consumes(MEDIATYPE_PROTOBUF) + @Produces(MEDIATYPE_PROTOBUF) + @Override + public Int64Value beginBatch(BeginBatchMessage message) { + try { + init(); + return bulletinBoard.beginBatch(message); + } catch (CommunicationException e) { + System.err.println(e.getMessage()); + return null; + } + } + + @Path(POST_BATCH_PATH) + @POST + @Consumes(MEDIATYPE_PROTOBUF) + @Produces(MEDIATYPE_PROTOBUF) + @Override + public BoolValue postBatchMessage(BatchMessage batchMessage) { + try { + init(); + return bulletinBoard.postBatchMessage(batchMessage); + } catch (CommunicationException e) { + System.err.println(e.getMessage()); + return null; + } + } + + @Path(CLOSE_BATCH_PATH) + @POST + @Consumes(MEDIATYPE_PROTOBUF) + @Produces(MEDIATYPE_PROTOBUF) + @Override + public BoolValue closeBatch(CloseBatchMessage message) { + try { + init(); + return bulletinBoard.closeBatch(message); + } catch (CommunicationException e) { + System.err.println(e.getMessage()); + return null; + } + } + + + @Override + public void readBatch(BatchQuery batchQuery, MessageOutputStream out) throws CommunicationException, IllegalArgumentException { + try { + init(); + bulletinBoard.readBatch(batchQuery, out); + } catch (CommunicationException | IllegalArgumentException e) { + System.err.println(e.getMessage()); + } + } + + @Path(GENERATE_SYNC_QUERY_PATH) + @POST + @Consumes(MEDIATYPE_PROTOBUF) + @Produces(MEDIATYPE_PROTOBUF) + @Override + public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { + try { + init(); + return bulletinBoard.generateSyncQuery(generateSyncQueryParams); + } catch (CommunicationException | IllegalArgumentException e) { + System.err.println(e.getMessage()); + return null; + } + } + + @Path(READ_BATCH_PATH) + @POST + @Consumes(MEDIATYPE_PROTOBUF) + /** + * Wrapper for the readBatch method which streams the output into the response + */ + public StreamingOutput readBatch(final BatchQuery batchQuery) { + + return new StreamingOutput() { + + @Override + public void write(OutputStream output) throws IOException, WebApplicationException { + MessageOutputStream out = new MessageOutputStream<>(output); + + try { + init(); + bulletinBoard.readBatch(batchQuery, out); + } catch (CommunicationException e) { + //TODO: Log + out.writeMessage(null); + } + } + + }; + + } + + @Path(SYNC_QUERY_PATH) + @POST + @Consumes(MEDIATYPE_PROTOBUF) + @Produces(MEDIATYPE_PROTOBUF) + @Override + public SyncQueryResponse querySync(SyncQuery syncQuery) throws CommunicationException { + try{ + init(); + return bulletinBoard.querySync(syncQuery); + } catch (CommunicationException | IllegalArgumentException e) { + System.err.println(e.getMessage()); + return null; + } + } + + @Override + public void close(){ + try { + bulletinBoard.close(); + } catch (CommunicationException e) { + System.err.println(e.getMessage()); + } + } + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String test() { + return "This BulletinBoard is up and running!\n Please consult the API documents to perform queries."; + } + + @Override + public void contextDestroyed(ServletContextEvent servletContextEvent) { + ServletContext servletContext = servletContextEvent.getServletContext(); + bulletinBoard = (BulletinBoardServer) servletContext.getAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME); + close(); + } + +} \ No newline at end of file diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/HelloProtoWebApp.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/HelloProtoWebApp.java index 469fba9..bf748c5 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/HelloProtoWebApp.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/HelloProtoWebApp.java @@ -1,50 +1,50 @@ -package meerkat.bulletinboard.webapp; - -import com.google.protobuf.ByteString; -import com.google.protobuf.Message; -import meerkat.bulletinboard.service.HelloProtoBuf; -import meerkat.protobuf.Crypto.*; -import meerkat.protobuf.BulletinBoardAPI.*; -import meerkat.rest.Constants; - -import javax.annotation.PostConstruct; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; - -@Path("/proto") -public class HelloProtoWebApp { - private HelloProtoBuf helloProtoBuf; - - @PostConstruct - public void init() { - helloProtoBuf = new HelloProtoBuf(); - } - - @GET - @Produces(Constants.MEDIATYPE_PROTOBUF) - public Message hello() { - byte[] b1 = { (byte) 1, (byte) 2, (byte) 3, (byte) 4 }; - byte[] b2 = { (byte) 11, (byte) 12, (byte) 13, (byte) 14 }; - byte[] b3 = {(byte) 21, (byte)22, (byte) 23, (byte) 24}; - - Message msg; - - if (helloProtoBuf != null) { - msg = helloProtoBuf.sayHello(); - } else { - msg = BulletinBoardMessage.newBuilder() - .setMsg(UnsignedBulletinBoardMessage.newBuilder() - .addTag("Signature") - .addTag("Trustee") - .setData(ByteString.copyFrom(b1)).build()) - .addSig(Signature.newBuilder() - .setType(SignatureType.DSA) - .setData(ByteString.copyFrom(b2)) - .setSignerId(ByteString.copyFrom(b3)).build()) - .build(); - } - - return msg; - } -} +package meerkat.bulletinboard.webapp; + +import com.google.protobuf.ByteString; +import com.google.protobuf.Message; +import meerkat.bulletinboard.service.HelloProtoBuf; +import meerkat.protobuf.Crypto.*; +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.rest.Constants; + +import javax.annotation.PostConstruct; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +@Path("/proto") +public class HelloProtoWebApp { + private HelloProtoBuf helloProtoBuf; + + @PostConstruct + public void init() { + helloProtoBuf = new HelloProtoBuf(); + } + + @GET + @Produces(Constants.MEDIATYPE_PROTOBUF) + public Message hello() { + byte[] b1 = { (byte) 1, (byte) 2, (byte) 3, (byte) 4 }; + byte[] b2 = { (byte) 11, (byte) 12, (byte) 13, (byte) 14 }; + byte[] b3 = {(byte) 21, (byte)22, (byte) 23, (byte) 24}; + + Message msg; + + if (helloProtoBuf != null) { + msg = helloProtoBuf.sayHello(); + } else { + msg = BulletinBoardMessage.newBuilder() + .setMsg(UnsignedBulletinBoardMessage.newBuilder() + .addTag("Signature") + .addTag("Trustee") + .setData(ByteString.copyFrom(b1)).build()) + .addSig(Signature.newBuilder() + .setType(SignatureType.DSA) + .setData(ByteString.copyFrom(b2)) + .setSignerId(ByteString.copyFrom(b3)).build()) + .build(); + } + + return msg; + } +} diff --git a/bulletin-board-server/src/main/proto/meerkat/bulletin_board_server.proto b/bulletin-board-server/src/main/proto/meerkat/bulletin_board_server.proto deleted file mode 100644 index c284302..0000000 --- a/bulletin-board-server/src/main/proto/meerkat/bulletin_board_server.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto3"; - -package meerkat; - -option java_package = "meerkat.protobuf"; - -message Boolean { - bool value = 1; -} \ No newline at end of file diff --git a/bulletin-board-server/src/main/webapp/META-INF/jetty-env.xml b/bulletin-board-server/src/main/webapp/META-INF/jetty-env.xml index 07c1470..c4d368f 100644 --- a/bulletin-board-server/src/main/webapp/META-INF/jetty-env.xml +++ b/bulletin-board-server/src/main/webapp/META-INF/jetty-env.xml @@ -1,12 +1,12 @@ - - - - - org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern - none - - - org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern - none - + + + + + org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern + none + + + org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern + none + \ No newline at end of file diff --git a/bulletin-board-server/src/main/webapp/WEB-INF/web.xml b/bulletin-board-server/src/main/webapp/WEB-INF/web.xml index 387ed62..226aa3b 100644 --- a/bulletin-board-server/src/main/webapp/WEB-INF/web.xml +++ b/bulletin-board-server/src/main/webapp/WEB-INF/web.xml @@ -1,38 +1,38 @@ - - - Jersey Hello World - - org.glassfish.jersey.servlet.ServletContainer - - - jersey.config.server.provider.packages - meerkat - - 1 - - - Jersey Hello World - /* - - - dbAddress - localhost - - dbPort - 3306 - - dbName - meerkat - - username - arbel - - password - mypass - - dbType - H2 - - meerkat.bulletinboard.webapp.BulletinBoardWebApp - - + + + Jersey Hello World + + org.glassfish.jersey.servlet.ServletContainer + + + jersey.config.server.provider.packages + meerkat + + 1 + + + Jersey Hello World + /* + + + dbAddress + localhost + + dbPort + 3306 + + dbName + meerkat + + username + arbel + + password + mypass + + dbType + H2 + + meerkat.bulletinboard.webapp.BulletinBoardWebApp + + diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/BulletinBoardSQLServerIntegrationTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/BulletinBoardSQLServerIntegrationTest.java index 844a050..8d5e0c0 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/BulletinBoardSQLServerIntegrationTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/BulletinBoardSQLServerIntegrationTest.java @@ -1,150 +1,150 @@ -package meerkat.bulletinboard; - - -import com.google.protobuf.BoolValue; -import com.google.protobuf.ByteString; -import com.google.protobuf.TextFormat; - -import com.google.protobuf.Timestamp; -import meerkat.comm.MessageInputStream; -import meerkat.protobuf.Crypto.*; -import meerkat.protobuf.BulletinBoardAPI.*; -import meerkat.protobuf.Comm.*; -import static meerkat.bulletinboard.BulletinBoardConstants.*; -import meerkat.rest.Constants; -import meerkat.rest.ProtobufMessageBodyReader; -import meerkat.rest.ProtobufMessageBodyWriter; - -import org.junit.Before; -import org.junit.Test; - -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.Response; -import java.io.InputStream; -import java.util.List; - -public class BulletinBoardSQLServerIntegrationTest { - - private static String PROP_GETTY_URL = "gretty.httpBaseURI"; - private static String DEFAULT_BASE_URL = "http://localhost:8081"; - private static String BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL); - - Client client; - - @Before - public void setup() throws Exception { - System.err.println("Registering client"); - client = ClientBuilder.newClient(); - client.register(ProtobufMessageBodyReader.class); - client.register(ProtobufMessageBodyWriter.class); - - } - - @Test - public void testPost() throws Exception { - byte[] b1 = {(byte) 1, (byte) 2, (byte) 3, (byte) 4}; - byte[] b2 = {(byte) 11, (byte) 12, (byte) 13, (byte) 14}; - byte[] b3 = {(byte) 21, (byte) 22, (byte) 23, (byte) 24}; - byte[] b4 = {(byte) 4, (byte) 5, (byte) 100, (byte) -50, (byte) 0}; - - Timestamp t1 = Timestamp.newBuilder() - .setSeconds(8276482) - .setNanos(4314) - .build(); - - Timestamp t2 = Timestamp.newBuilder() - .setSeconds(987591) - .setNanos(1513) - .build(); - - WebTarget webTarget; - Response response; - BoolValue bool; - - BulletinBoardMessage msg; - - MessageFilterList filterList; - List msgList; - - // Test writing mechanism - - System.err.println("******** Testing: " + POST_MESSAGE_PATH); - webTarget = client.target(BASE_URL).path(BULLETIN_BOARD_SERVER_PATH).path(POST_MESSAGE_PATH); - System.err.println(webTarget.getUri()); - - msg = BulletinBoardMessage.newBuilder() - .setMsg(UnsignedBulletinBoardMessage.newBuilder() - .addTag("Signature") - .addTag("Trustee") - .setData(ByteString.copyFrom(b1)) - .setTimestamp(t1) - .build()) - .addSig(Signature.newBuilder() - .setType(SignatureType.DSA) - .setData(ByteString.copyFrom(b2)) - .setSignerId(ByteString.copyFrom(b3)) - .build()) - .addSig(Signature.newBuilder() - .setType(SignatureType.ECDSA) - .setData(ByteString.copyFrom(b3)) - .setSignerId(ByteString.copyFrom(b2)) - .build()) - .build(); - - response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF)); - System.err.println(response); - bool = response.readEntity(BoolValue.class); - assert bool.getValue(); - - msg = BulletinBoardMessage.newBuilder() - .setMsg(UnsignedBulletinBoardMessage.newBuilder() - .addTag("Vote") - .addTag("Trustee") - .setData(ByteString.copyFrom(b4)) - .setTimestamp(t2) - .build()) - .addSig(Signature.newBuilder() - .setType(SignatureType.ECDSA) - .setData(ByteString.copyFrom(b4)) - .setSignerId(ByteString.copyFrom(b2)) - .build()) - .build(); - - response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF)); - System.err.println(response); - bool = response.readEntity(BoolValue.class); - assert bool.getValue(); - - // Test reading mechanism - - System.err.println("******** Testing: " + READ_MESSAGES_PATH); - webTarget = client.target(BASE_URL).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH); - filterList = MessageFilterList.newBuilder() - .addFilter( - MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag("Vote") - .build() - ) - .build(); - - InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF), InputStream.class); - - MessageInputStream inputStream = - MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BulletinBoardMessage.class); - - msgList = inputStream.asList(); - System.err.println("List size: " + msgList.size()); - System.err.println("This is the list:"); - - for (BulletinBoardMessage message : msgList) { - System.err.println(TextFormat.printToString(message)); - } - - assert msgList.size() == 1; - } - -} +package meerkat.bulletinboard; + + +import com.google.protobuf.BoolValue; +import com.google.protobuf.ByteString; +import com.google.protobuf.TextFormat; + +import com.google.protobuf.Timestamp; +import meerkat.comm.MessageInputStream; +import meerkat.protobuf.Crypto.*; +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Comm.*; +import static meerkat.bulletinboard.BulletinBoardConstants.*; +import meerkat.rest.Constants; +import meerkat.rest.ProtobufMessageBodyReader; +import meerkat.rest.ProtobufMessageBodyWriter; + +import org.junit.Before; +import org.junit.Test; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; +import java.io.InputStream; +import java.util.List; + +public class BulletinBoardSQLServerIntegrationTest { + + private static String PROP_GETTY_URL = "gretty.httpBaseURI"; + private static String DEFAULT_BASE_URL = "http://localhost:8081"; + private static String BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL); + + Client client; + + @Before + public void setup() throws Exception { + System.err.println("Registering client"); + client = ClientBuilder.newClient(); + client.register(ProtobufMessageBodyReader.class); + client.register(ProtobufMessageBodyWriter.class); + + } + + @Test + public void testPost() throws Exception { + byte[] b1 = {(byte) 1, (byte) 2, (byte) 3, (byte) 4}; + byte[] b2 = {(byte) 11, (byte) 12, (byte) 13, (byte) 14}; + byte[] b3 = {(byte) 21, (byte) 22, (byte) 23, (byte) 24}; + byte[] b4 = {(byte) 4, (byte) 5, (byte) 100, (byte) -50, (byte) 0}; + + Timestamp t1 = Timestamp.newBuilder() + .setSeconds(8276482) + .setNanos(4314) + .build(); + + Timestamp t2 = Timestamp.newBuilder() + .setSeconds(987591) + .setNanos(1513) + .build(); + + WebTarget webTarget; + Response response; + BoolValue bool; + + BulletinBoardMessage msg; + + MessageFilterList filterList; + List msgList; + + // Test writing mechanism + + System.err.println("******** Testing: " + POST_MESSAGE_PATH); + webTarget = client.target(BASE_URL).path(BULLETIN_BOARD_SERVER_PATH).path(POST_MESSAGE_PATH); + System.err.println(webTarget.getUri()); + + msg = BulletinBoardMessage.newBuilder() + .setMsg(UnsignedBulletinBoardMessage.newBuilder() + .addTag("Signature") + .addTag("Trustee") + .setData(ByteString.copyFrom(b1)) + .setTimestamp(t1) + .build()) + .addSig(Signature.newBuilder() + .setType(SignatureType.DSA) + .setData(ByteString.copyFrom(b2)) + .setSignerId(ByteString.copyFrom(b3)) + .build()) + .addSig(Signature.newBuilder() + .setType(SignatureType.ECDSA) + .setData(ByteString.copyFrom(b3)) + .setSignerId(ByteString.copyFrom(b2)) + .build()) + .build(); + + response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF)); + System.err.println(response); + bool = response.readEntity(BoolValue.class); + assert bool.getValue(); + + msg = BulletinBoardMessage.newBuilder() + .setMsg(UnsignedBulletinBoardMessage.newBuilder() + .addTag("Vote") + .addTag("Trustee") + .setData(ByteString.copyFrom(b4)) + .setTimestamp(t2) + .build()) + .addSig(Signature.newBuilder() + .setType(SignatureType.ECDSA) + .setData(ByteString.copyFrom(b4)) + .setSignerId(ByteString.copyFrom(b2)) + .build()) + .build(); + + response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF)); + System.err.println(response); + bool = response.readEntity(BoolValue.class); + assert bool.getValue(); + + // Test reading mechanism + + System.err.println("******** Testing: " + READ_MESSAGES_PATH); + webTarget = client.target(BASE_URL).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH); + filterList = MessageFilterList.newBuilder() + .addFilter( + MessageFilter.newBuilder() + .setType(FilterType.TAG) + .setTag("Vote") + .build() + ) + .build(); + + InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF), InputStream.class); + + MessageInputStream inputStream = + MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BulletinBoardMessage.class); + + msgList = inputStream.asList(); + System.err.println("List size: " + msgList.size()); + System.err.println("This is the list:"); + + for (BulletinBoardMessage message : msgList) { + System.err.println(TextFormat.printToString(message)); + } + + assert msgList.size() == 1; + } + +} diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/GenericBulletinBoardServerTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/GenericBulletinBoardServerTest.java index 49c3050..eafd80c 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/GenericBulletinBoardServerTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/GenericBulletinBoardServerTest.java @@ -1,716 +1,657 @@ -package meerkat.bulletinboard; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadMXBean; -import java.lang.reflect.InvocationTargetException; -import java.math.BigInteger; -import java.security.InvalidKeyException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.SignatureException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.util.*; - -import com.google.protobuf.BoolValue; -import com.google.protobuf.ByteString; - -import com.google.protobuf.Timestamp; -import meerkat.comm.CommunicationException; -import meerkat.comm.MessageInputStream; -import meerkat.comm.MessageOutputStream; -import meerkat.comm.MessageInputStream.MessageInputStreamFactory; -import meerkat.crypto.Digest; -import meerkat.crypto.concrete.ECDSASignature; -import meerkat.crypto.concrete.SHA256Digest; -import meerkat.protobuf.BulletinBoardAPI.*; -import meerkat.protobuf.Comm.*; -import meerkat.util.BulletinBoardMessageGenerator; - -import static org.junit.Assert.*; -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; - -public class GenericBulletinBoardServerTest { - - protected BulletinBoardServer bulletinBoardServer; - private GenericBatchDigitalSignature[] signers; - private ByteString[] signerIDs; - - private Random random; - - private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12"; - private static String KEYFILE_EXAMPLE3 = "/certs/enduser-certs/user3-key-with-password-shh.p12"; - - private static String KEYFILE_PASSWORD1 = "secret"; - private static String KEYFILE_PASSWORD3 = "shh"; - - public static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt"; - public static String CERT3_PEM_EXAMPLE = "/certs/enduser-certs/user3.crt"; - - private final int TAG_NUM = 5; // Number of tags. - private final int MESSAGE_NUM = 32; // Number of messages (2^TAG_NUM). - - private String[] tags; - private byte[][] data; - - private List completeBatches; - - private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests - - private BulletinBoardMessageGenerator bulletinBoardMessageGenerator; - - private Digest digest; - - /** - * @param bulletinBoardServer is an initialized server. - * @throws InstantiationException - * @throws IllegalAccessException - * @throws CertificateException - * @throws KeyStoreException - * @throws NoSuchAlgorithmException - * @throws IOException - * @throws UnrecoverableKeyException - * @throws CommunicationException - */ - public void init(BulletinBoardServer bulletinBoardServer) { - - System.err.println("Starting to initialize GenericBulletinBoardServerTest"); - long start = threadBean.getCurrentThreadCpuTime(); - - this.bulletinBoardServer = bulletinBoardServer; - - signers = new GenericBatchDigitalSignature[2]; - signerIDs = new ByteString[signers.length]; - signers[0] = new GenericBatchDigitalSignature(new ECDSASignature()); - signers[1] = new GenericBatchDigitalSignature(new ECDSASignature()); - - InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE); - char[] password = KEYFILE_PASSWORD1.toCharArray(); - - KeyStore.Builder keyStoreBuilder = null; - try { - keyStoreBuilder = signers[0].getPKCS12KeyStoreBuilder(keyStream, password); - - signers[0].loadSigningCertificate(keyStoreBuilder); - - signers[0].loadVerificationCertificates(getClass().getResourceAsStream(CERT1_PEM_EXAMPLE)); - - keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE3); - password = KEYFILE_PASSWORD3.toCharArray(); - - keyStoreBuilder = signers[1].getPKCS12KeyStoreBuilder(keyStream, password); - signers[1].loadSigningCertificate(keyStoreBuilder); - - signers[1].loadVerificationCertificates(getClass().getResourceAsStream(CERT3_PEM_EXAMPLE)); - - for (int i = 0 ; i < signers.length ; i++) { - signerIDs[i] = signers[i].getSignerID(); - } - - } 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()); - } - - // We use insecure randomness in tests for repeatability - random = new Random(0); - bulletinBoardMessageGenerator = new BulletinBoardMessageGenerator(random); - - digest = new SHA256Digest(); - - long end = threadBean.getCurrentThreadCpuTime(); - System.err.println("Finished initializing GenericBulletinBoardServerTest"); - System.err.println("Time of operation: " + (end - start)); - - // Initialize Batch variables - - completeBatches = new ArrayList(10); - - } - - private byte randomByte(){ - return (byte) random.nextInt(); - } - - private String randomString(){ - return new BigInteger(130, random).toString(32); - } - - /** - * Tests writing of several messages with multiple tags and signatures. - * @throws CommunicationException - * @throws SignatureException - * @throws InvalidKeyException - * @throws CertificateException - * @throws IOException - */ - public void testInsert() { - - System.err.println("Starting to insert messages to DB"); - long start = threadBean.getCurrentThreadCpuTime(); - - final int BYTES_PER_MESSAGE_DATA = 50; // Message size. - - tags = new String[TAG_NUM]; - data = new byte[MESSAGE_NUM][BYTES_PER_MESSAGE_DATA]; - - UnsignedBulletinBoardMessage.Builder unsignedMsgBuilder; - BulletinBoardMessage.Builder msgBuilder; - - int i, j; - - // Generate random data. - - for (i = 1; i <= MESSAGE_NUM; i++) { - for (j = 0; j < BYTES_PER_MESSAGE_DATA; j++) { - data[i - 1][j] = randomByte(); - } - } - - for (i = 0; i < TAG_NUM; i++) { - tags[i] = randomString(); - } - - // Build messages. - - for (i = 1; i <= MESSAGE_NUM; i++) { - unsignedMsgBuilder = UnsignedBulletinBoardMessage.newBuilder() - .setData(ByteString.copyFrom(data[i - 1])) - .setTimestamp(Timestamp.newBuilder() - .setSeconds(i) - .setNanos(i) - .build()); - - // Add tags based on bit-representation of message number. - - int copyI = i; - for (j = 0; j < TAG_NUM; j++) { - if (copyI % 2 == 1) { - unsignedMsgBuilder.addTag(tags[j]); - } - - copyI >>>= 1; - } - - // Build message. - - msgBuilder = BulletinBoardMessage.newBuilder() - .setMsg(unsignedMsgBuilder.build()); - - // Add signatures. - - try { - - if (i % 2 == 1) { - signers[0].updateContent(msgBuilder.getMsg()); - msgBuilder.addSig(signers[0].sign()); - - if (i % 4 == 1) { - signers[1].updateContent(msgBuilder.getMsg()); - msgBuilder.addSig(signers[1].sign()); - } - } - - } catch (SignatureException e) { - fail(e.getMessage()); - } - - // Post message. - - try { - bulletinBoardServer.postMessage(msgBuilder.build()); - } catch (CommunicationException e) { - fail(e.getMessage()); - } - } - - long end = threadBean.getCurrentThreadCpuTime(); - System.err.println("Finished inserting messages to DB"); - System.err.println("Time of operation: " + (end - start)); - - } - - /** - * Tests retrieval of messages written in {@Link #testInsert()} - * Only queries using one tag filter - */ - public void testSimpleTagAndSignature(){ - - System.err.println("Starting to test tag and signature mechanism"); - long start = threadBean.getCurrentThreadCpuTime(); - - List messages = new LinkedList<>(); - - // Check tag mechanism - - for (int i = 0 ; i < TAG_NUM ; i++){ - - // Retrieve messages having tag i - - try { - - MessageFilterList filterList = MessageFilterList.newBuilder() - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(tags[i]) - .build() - ) - .build(); - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - bulletinBoardServer.readMessages(filterList, new MessageOutputStream(outputStream)); - - MessageInputStream inputStream = - MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream( - outputStream.toByteArray()), - BulletinBoardMessage.class); - - messages = inputStream.asList(); - - } catch (CommunicationException | IOException e) { - fail(e.getMessage()); - return; - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - fail(e.getMessage()); - return; - } - - // Assert that the number of retrieved messages is correct. - - assertThat(messages.size(), is(MESSAGE_NUM / 2)); - - // Assert the identity of the messages. - - for (BulletinBoardMessage msg : messages){ - - // Assert serial number and raw data. - - assertThat((msg.getEntryNum() >>> i) % 2 , is((long) 1)); - assertThat(msg.getMsg().getData().toByteArray(), is(data[(int) msg.getEntryNum() - 1])); - - // Assert signatures. - - try { - - if (msg.getEntryNum() % 2 == 1) { - signers[0].initVerify(msg.getSig(0)); - signers[0].updateContent(msg.getMsg()); - assertTrue("Signature did not verify!", signers[0].verify()); - - if (msg.getEntryNum() % 4 == 1) { - signers[1].initVerify(msg.getSig(1)); - signers[1].updateContent(msg.getMsg()); - assertTrue("Signature did not verify!", signers[1].verify()); - - assertThat(msg.getSigCount(), is(2)); - } else { - assertThat(msg.getSigCount(), is(1)); - } - } else { - assertThat(msg.getSigCount(), is(0)); - } - } catch (Exception e) { - fail(e.getMessage()); - } - } - - } - - long end = threadBean.getCurrentThreadCpuTime(); - System.err.println("Finished testing tag and signature mechanism"); - System.err.println("Time of operation: " + (end - start)); - - } - - /** - * Tests retrieval of messages written in {@Link #testInsert()} using multiple tags/signature filters. - */ - public void testEnhancedTagsAndSignatures(){ - - System.err.println("Starting to test multiple tags and signatures"); - long start = threadBean.getCurrentThreadCpuTime(); - - List messages; - MessageFilterList.Builder filterListBuilder = MessageFilterList.newBuilder(); - - int expectedMsgCount = MESSAGE_NUM; - - // Check multiple tag filters. - - for (int i = 0 ; i < TAG_NUM ; i++) { - - filterListBuilder.addFilter( - MessageFilter.newBuilder() - .setType(FilterType.TAG) - .setTag(tags[i]) - .build() - ); - - try { - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - bulletinBoardServer.readMessages(filterListBuilder.build(), new MessageOutputStream(outputStream)); - - MessageInputStream inputStream = - MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream( - outputStream.toByteArray()), - BulletinBoardMessage.class); - - messages = inputStream.asList(); - - } catch (CommunicationException e) { - System.err.println("Failed retrieving multi-tag messages from DB: " + e.getMessage()); - fail("Failed retrieving multi-tag messages from DB: " + e.getMessage()); - return; - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | IOException e) { - System.err.println("Falied to read from stream while retrieving multi-tag messages: " + e.getMessage()); - fail("Falied to read from stream while retrieving multi-tag messages: " + e.getMessage()); - return; - } - - expectedMsgCount /= 2; - - assertThat(messages.size(), is(expectedMsgCount)); - - for (BulletinBoardMessage msg : messages) { - for (int j = 0 ; j <= i ; j++) { - assertThat((msg.getEntryNum() >>> j) % 2, is((long) 1)); - } - } - } - - // Check multiple signature filters. - - filterListBuilder = MessageFilterList.newBuilder() - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.SIGNER_ID) - .setId(signerIDs[0]) - .build()) - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.SIGNER_ID) - .setId(signerIDs[1]) - .build()); - - try { - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - bulletinBoardServer.readMessages(filterListBuilder.build(), new MessageOutputStream(outputStream)); - - MessageInputStream inputStream = - MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream( - outputStream.toByteArray()), - BulletinBoardMessage.class); - - messages = inputStream.asList(); - - } catch (CommunicationException e) { - System.err.println("Failed retrieving multi-signature message from DB: " + e.getMessage()); - fail("Failed retrieving multi-signature message from DB: " + e.getMessage()); - return; - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | IOException e) { - System.err.println("Falied to read from stream while retrieving multi-signature message: " + e.getMessage()); - fail("Falied to read from stream while retrieving multi-signature message: " + e.getMessage()); - return; - } - - assertThat(messages.size(), is(MESSAGE_NUM / 4)); - - for (BulletinBoardMessage message : messages) { - assertThat(message.getEntryNum() % 4, is((long) 1)); - } - - long end = threadBean.getCurrentThreadCpuTime(); - System.err.println("Finished testing multiple tags and signatures"); - System.err.println("Time of operation: " + (end - start)); - - } - - /** - * Tests that posting a message before opening a batch does not work - * @throws CommunicationException - */ - public void testBatchPostAfterClose() throws CommunicationException, SignatureException { - - final int BATCH_ID = 100; - - CompleteBatch completeBatch = new CompleteBatch(Timestamp.newBuilder() - .setSeconds(978325) - .setNanos(8097234) - .build()); - BoolValue result; - - // Create data - - completeBatch.setBeginBatchMessage(BeginBatchMessage.newBuilder() - .setSignerId(signerIDs[1]) - .setBatchId(BATCH_ID) - .addTag("Test") - .build()); - - BatchData batchData = BatchData.newBuilder() - .setData(ByteString.copyFrom((new byte[] {1,2,3,4}))) - .build(); - - completeBatch.appendBatchData(batchData); - - signers[1].updateContent(completeBatch); - - completeBatch.setSignature(signers[1].sign()); - - // Begin batch - - result = bulletinBoardServer.beginBatch(completeBatch.getBeginBatchMessage()); - - assertThat("Was not able to open batch", result.getValue(), is(true)); - - // Post data - - BatchMessage batchMessage = BatchMessage.newBuilder() - .setSignerId(signerIDs[1]) - .setBatchId(BATCH_ID) - .setData(batchData) - .build(); - - result = bulletinBoardServer.postBatchMessage(batchMessage); - - assertThat("Was not able to post batch message", result.getValue(), is(true)); - - // Close batch - - result = bulletinBoardServer.closeBatchMessage(completeBatch.getCloseBatchMessage()); - - assertThat("Was not able to close batch", result.getValue(), is(true)); - - // Attempt to open batch again - - result = bulletinBoardServer.beginBatch(completeBatch.getBeginBatchMessage()); - - assertThat("Was able to open a closed batch", result.getValue(), is(false)); - - // Attempt to add batch data - - result = bulletinBoardServer.postBatchMessage(batchMessage); - - assertThat("Was able to change a closed batch", result.getValue(), is(false)); - - } - - /** - * Posts a complete batch message - * @throws CommunicationException - */ - public void testPostBatch() throws CommunicationException, SignatureException { - - CompleteBatch completeBatch = new CompleteBatch(Timestamp.newBuilder() - .setSeconds(12345) - .setNanos(1111) - .build()); - int currentBatch = completeBatches.size(); - - BoolValue result; - - // Define batch data - - String[] tempBatchTags = new String[]{randomString(),randomString(),randomString()}; - byte[][] tempBatchData = new byte[Math.abs(randomByte())][]; - - for (int i = 0 ; i < tempBatchData.length ; i++) { - - tempBatchData[i] = new byte[Math.abs(randomByte())]; - - for (int j = 0; j < tempBatchData[i].length; j++) { - tempBatchData[i][j] = randomByte(); - } - - } - - // Begin batch - - completeBatch.setBeginBatchMessage(BeginBatchMessage.newBuilder() - .setSignerId(signerIDs[0]) - .setBatchId(currentBatch) - .addAllTag(Arrays.asList(tempBatchTags)) - .build()); - - result = bulletinBoardServer.beginBatch(completeBatch.getBeginBatchMessage()); - - assertThat("Could not begin batch " + currentBatch, result.getValue(), is(true)); - - // Add batch data and randomize data posting order - - List dataOrder = new ArrayList(tempBatchData.length); - for (int i = 0 ; i < tempBatchData.length ; i++) { - dataOrder.add(i); - completeBatch.appendBatchData(BatchData.newBuilder() - .setData(ByteString.copyFrom(tempBatchData[i])) - .build()); - } - Collections.shuffle(dataOrder); - - // Post data - - for (int i = 0 ; i < tempBatchData.length ; i++) { - - int dataIndex = dataOrder.get(i); - - result = bulletinBoardServer.postBatchMessage(BatchMessage.newBuilder() - .setSignerId(signerIDs[0]) - .setBatchId(currentBatch) - .setSerialNum(dataIndex) - .setData(completeBatch.getBatchDataList().get(dataIndex)) - .build()); - - assertThat("Could not post batch data for batch ID " + currentBatch + " serial number " + dataIndex, - result.getValue(), is(true)); - - } - - // Close batch - - signers[0].updateContent(completeBatch); - completeBatch.setSignature(signers[0].sign()); - - result = bulletinBoardServer.closeBatchMessage(completeBatch.getCloseBatchMessage()); - - assertThat("Could not close batch " + currentBatch, result.getValue(), is(true)); - - // Update locally stored batches - completeBatches.add(completeBatch); - - } - - public void testReadBatch() throws CommunicationException { - - for (CompleteBatch completeBatch : completeBatches) { - - try { - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - BatchSpecificationMessage batchSpecificationMessage = - BatchSpecificationMessage.newBuilder() - .setSignerId(completeBatch.getBeginBatchMessage().getSignerId()) - .setBatchId(completeBatch.getBeginBatchMessage().getBatchId()) - .setStartPosition(0) - .build(); - - bulletinBoardServer.readBatch(batchSpecificationMessage, new MessageOutputStream(outputStream)); - - MessageInputStream inputStream = - MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream( - outputStream.toByteArray()), - BatchData.class); - - List batchDataList = inputStream.asList(); - - assertThat("Non-matching batch data for batch " + completeBatch.getBeginBatchMessage().getBatchId(), - completeBatch.getBatchDataList().equals(batchDataList), is(true)); - - } catch (IOException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - assertThat("Error reading batch data list from input stream", false); - } - - } - - } - - public void testSyncQuery() - throws SignatureException, CommunicationException, IOException,NoSuchMethodException, IllegalAccessException, InvocationTargetException { - - Checksum checksum = new SimpleChecksum(); - - Timestamp timestamp = Timestamp.newBuilder() - .setSeconds(1) - .setNanos(0) - .build(); - - BulletinBoardMessage newMessage = bulletinBoardMessageGenerator.generateRandomMessage(signers, timestamp, 10, 10); - - BoolValue result = bulletinBoardServer.postMessage(newMessage); - assertThat("Failed to post message to BB Server", result.getValue(), is(true)); - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - digest.update(newMessage.getMsg()); - - ByteString messageID = ByteString.copyFrom(digest.digest()); - - MessageFilterList filterList = MessageFilterList.newBuilder() - .addFilter(MessageFilter.newBuilder() - .setType(FilterType.MSG_ID) - .setId(messageID) - .build()) - .build(); - - bulletinBoardServer.readMessages(filterList, new MessageOutputStream(outputStream)); - - MessageInputStream inputStream = - MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream( - outputStream.toByteArray()), - BulletinBoardMessage.class); - - long lastEntry = inputStream.asList().get(0).getEntryNum(); - - SyncQuery syncQuery = SyncQuery.newBuilder() - .setFilterList(MessageFilterList.getDefaultInstance()) - .addQuery(SingleSyncQuery.newBuilder() - .setChecksum(2) - .setTimeOfSync(Timestamp.newBuilder() - .setSeconds(2) - .setNanos(0) - .build()) - .build()) - .build(); - - SyncQueryResponse queryResponse = bulletinBoardServer.querySync(syncQuery); - - assertThat("Sync query replies with positive sync when no sync was expected", queryResponse.getLastEntryNum(), is(equalTo(-1l))); - - syncQuery = SyncQuery.newBuilder() - .setFilterList(MessageFilterList.getDefaultInstance()) - .addQuery(SingleSyncQuery.newBuilder() - .setChecksum(checksum.getChecksum(messageID)) - .setTimeOfSync(timestamp) - .build()) - .build(); - - queryResponse = bulletinBoardServer.querySync(syncQuery); - - assertThat("Sync query reply contained wrong last entry number", lastEntry, is(equalTo(queryResponse.getLastEntryNum()))); - - assertThat("Sync query reply contained wrong timestamp", timestamp, is(equalTo(queryResponse.getLastTimeOfSync()))); - - } - - public void close(){ - signers[0].clearSigningKey(); - signers[1].clearSigningKey(); - try { - bulletinBoardServer.close(); - } catch (CommunicationException e) { - System.err.println("Error closing server " + e.getMessage()); - fail("Error closing server " + e.getMessage()); - } - } -} +package meerkat.bulletinboard; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.*; + +import com.google.protobuf.BoolValue; +import com.google.protobuf.ByteString; + +import com.google.protobuf.Int64Value; +import com.google.protobuf.Timestamp; +import meerkat.comm.CommunicationException; +import meerkat.comm.MessageInputStream; +import meerkat.comm.MessageOutputStream; +import meerkat.comm.MessageInputStream.MessageInputStreamFactory; +import meerkat.crypto.concrete.ECDSASignature; +import meerkat.crypto.concrete.SHA256Digest; +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.util.BulletinBoardMessageComparator; +import meerkat.util.BulletinBoardMessageGenerator; +import meerkat.util.BulletinBoardUtils; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; + +public class GenericBulletinBoardServerTest { + + protected BulletinBoardServer bulletinBoardServer; + private GenericBulletinBoardSignature[] signers; + private ByteString[] signerIDs; + + private Random random; + + private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12"; + private static String KEYFILE_EXAMPLE3 = "/certs/enduser-certs/user3-key-with-password-shh.p12"; + + private static String KEYFILE_PASSWORD1 = "secret"; + private static String KEYFILE_PASSWORD3 = "shh"; + + public static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt"; + public static String CERT3_PEM_EXAMPLE = "/certs/enduser-certs/user3.crt"; + + private final int TAG_NUM = 5; // Number of tags. + private final int MESSAGE_NUM = 32; // Number of messages (2^TAG_NUM). + + private String[] tags; + private byte[][] data; + + private List batches; + + private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests + + private BulletinBoardMessageGenerator bulletinBoardMessageGenerator; + + private BulletinBoardDigest digest; + + private BulletinBoardMessageComparator comparator; + + /** + * @param bulletinBoardServer is an initialized server. + */ + public void init(BulletinBoardServer bulletinBoardServer) { + + System.err.println("Starting to initialize GenericBulletinBoardServerTest"); + long start = threadBean.getCurrentThreadCpuTime(); + + this.bulletinBoardServer = bulletinBoardServer; + + signers = new GenericBulletinBoardSignature[2]; + signerIDs = new ByteString[signers.length]; + signers[0] = new GenericBulletinBoardSignature(new ECDSASignature()); + signers[1] = new GenericBulletinBoardSignature(new ECDSASignature()); + + InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE); + char[] password = KEYFILE_PASSWORD1.toCharArray(); + + KeyStore.Builder keyStoreBuilder = null; + try { + keyStoreBuilder = signers[0].getPKCS12KeyStoreBuilder(keyStream, password); + + signers[0].loadSigningCertificate(keyStoreBuilder); + + signers[0].loadVerificationCertificates(getClass().getResourceAsStream(CERT1_PEM_EXAMPLE)); + + keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE3); + password = KEYFILE_PASSWORD3.toCharArray(); + + keyStoreBuilder = signers[1].getPKCS12KeyStoreBuilder(keyStream, password); + signers[1].loadSigningCertificate(keyStoreBuilder); + + signers[1].loadVerificationCertificates(getClass().getResourceAsStream(CERT3_PEM_EXAMPLE)); + + for (int i = 0 ; i < signers.length ; i++) { + signerIDs[i] = signers[i].getSignerID(); + } + + } 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()); + } + + // We use insecure randomness in tests for repeatability + random = new Random(0); + bulletinBoardMessageGenerator = new BulletinBoardMessageGenerator(random); + + digest = new GenericBulletinBoardDigest(new SHA256Digest()); + + comparator = new BulletinBoardMessageComparator(); + + long end = threadBean.getCurrentThreadCpuTime(); + System.err.println("Finished initializing GenericBulletinBoardServerTest"); + System.err.println("Time of operation: " + (end - start)); + + // Initialize Batch variables + + batches = new ArrayList<>(10); + + } + + private byte randomByte(){ + return (byte) random.nextInt(); + } + + private String randomString(){ + return new BigInteger(130, random).toString(32); + } + + /** + * Tests writing of several messages with multiple tags and signatures. + * @throws CommunicationException + * @throws SignatureException + * @throws InvalidKeyException + * @throws CertificateException + * @throws IOException + */ + public void testInsert() { + + System.err.println("Starting to insert messages to DB"); + long start = threadBean.getCurrentThreadCpuTime(); + + final int BYTES_PER_MESSAGE_DATA = 50; // Message size. + + tags = new String[TAG_NUM]; + data = new byte[MESSAGE_NUM][BYTES_PER_MESSAGE_DATA]; + + UnsignedBulletinBoardMessage.Builder unsignedMsgBuilder; + BulletinBoardMessage.Builder msgBuilder; + + int i, j; + + // Generate random data. + + for (i = 1; i <= MESSAGE_NUM; i++) { + for (j = 0; j < BYTES_PER_MESSAGE_DATA; j++) { + data[i - 1][j] = randomByte(); + } + } + + for (i = 0; i < TAG_NUM; i++) { + tags[i] = randomString(); + } + + // Build messages. + + for (i = 1; i <= MESSAGE_NUM; i++) { + unsignedMsgBuilder = UnsignedBulletinBoardMessage.newBuilder() + .setData(ByteString.copyFrom(data[i - 1])) + .setTimestamp(Timestamp.newBuilder() + .setSeconds(i) + .setNanos(i) + .build()); + + // Add tags based on bit-representation of message number. + + int copyI = i; + for (j = 0; j < TAG_NUM; j++) { + if (copyI % 2 == 1) { + unsignedMsgBuilder.addTag(tags[j]); + } + + copyI >>>= 1; + } + + // Build message. + + msgBuilder = BulletinBoardMessage.newBuilder() + .setMsg(unsignedMsgBuilder.build()); + + // Add signatures. + + try { + + if (i % 2 == 1) { + signers[0].updateContent(msgBuilder.getMsg()); + msgBuilder.addSig(signers[0].sign()); + + if (i % 4 == 1) { + signers[1].updateContent(msgBuilder.getMsg()); + msgBuilder.addSig(signers[1].sign()); + } + } + + } catch (SignatureException e) { + fail(e.getMessage()); + } + + // Post message. + + try { + bulletinBoardServer.postMessage(msgBuilder.build()); + } catch (CommunicationException e) { + fail(e.getMessage()); + } + } + + long end = threadBean.getCurrentThreadCpuTime(); + System.err.println("Finished inserting messages to DB"); + System.err.println("Time of operation: " + (end - start)); + + } + + /** + * Tests retrieval of messages written in {@Link #testInsert()} + * Only queries using one tag filter + */ + public void testSimpleTagAndSignature(){ + + System.err.println("Starting to test tag and signature mechanism"); + long start = threadBean.getCurrentThreadCpuTime(); + + List messages = new LinkedList<>(); + + // Check tag mechanism + + for (int i = 0 ; i < TAG_NUM ; i++){ + + // Retrieve messages having tag i + + try { + + MessageFilterList filterList = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.TAG) + .setTag(tags[i]) + .build() + ) + .build(); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + bulletinBoardServer.readMessages(filterList, new MessageOutputStream(outputStream)); + + MessageInputStream inputStream = + MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream( + outputStream.toByteArray()), + BulletinBoardMessage.class); + + messages = inputStream.asList(); + + } catch (CommunicationException | IOException e) { + fail(e.getMessage()); + return; + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + fail(e.getMessage()); + return; + } + + // Assert that the number of retrieved messages is correct. + + assertThat(messages.size(), is(MESSAGE_NUM / 2)); + + // Assert the identity of the messages. + + for (BulletinBoardMessage msg : messages){ + + // Assert serial number and raw data. + + assertThat((msg.getEntryNum() >>> i) % 2 , is((long) 1)); + assertThat(msg.getMsg().getData().toByteArray(), is(data[(int) msg.getEntryNum() - 1])); + + // Assert signatures. + + try { + + if (msg.getEntryNum() % 2 == 1) { + signers[0].initVerify(msg.getSig(0)); + signers[0].updateContent(msg.getMsg()); + assertTrue("Signature did not verify!", signers[0].verify()); + + if (msg.getEntryNum() % 4 == 1) { + signers[1].initVerify(msg.getSig(1)); + signers[1].updateContent(msg.getMsg()); + assertTrue("Signature did not verify!", signers[1].verify()); + + assertThat(msg.getSigCount(), is(2)); + } else { + assertThat(msg.getSigCount(), is(1)); + } + } else { + assertThat(msg.getSigCount(), is(0)); + } + } catch (Exception e) { + fail(e.getMessage()); + } + } + + } + + long end = threadBean.getCurrentThreadCpuTime(); + System.err.println("Finished testing tag and signature mechanism"); + System.err.println("Time of operation: " + (end - start)); + + } + + /** + * Tests retrieval of messages written in {@Link #testInsert()} using multiple tags/signature filters. + */ + public void testEnhancedTagsAndSignatures(){ + + System.err.println("Starting to test multiple tags and signatures"); + long start = threadBean.getCurrentThreadCpuTime(); + + List messages; + MessageFilterList.Builder filterListBuilder = MessageFilterList.newBuilder(); + + int expectedMsgCount = MESSAGE_NUM; + + // Check multiple tag filters. + + for (int i = 0 ; i < TAG_NUM ; i++) { + + filterListBuilder.addFilter( + MessageFilter.newBuilder() + .setType(FilterType.TAG) + .setTag(tags[i]) + .build() + ); + + try { + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + bulletinBoardServer.readMessages(filterListBuilder.build(), new MessageOutputStream(outputStream)); + + MessageInputStream inputStream = + MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream( + outputStream.toByteArray()), + BulletinBoardMessage.class); + + messages = inputStream.asList(); + + } catch (CommunicationException e) { + System.err.println("Failed retrieving multi-tag messages from DB: " + e.getMessage()); + fail("Failed retrieving multi-tag messages from DB: " + e.getMessage()); + return; + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | IOException e) { + System.err.println("Falied to read from stream while retrieving multi-tag messages: " + e.getMessage()); + fail("Falied to read from stream while retrieving multi-tag messages: " + e.getMessage()); + return; + } + + expectedMsgCount /= 2; + + assertThat(messages.size(), is(expectedMsgCount)); + + for (BulletinBoardMessage msg : messages) { + for (int j = 0 ; j <= i ; j++) { + assertThat((msg.getEntryNum() >>> j) % 2, is((long) 1)); + } + } + } + + // Check multiple signature filters. + + filterListBuilder = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.SIGNER_ID) + .setId(signerIDs[0]) + .build()) + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.SIGNER_ID) + .setId(signerIDs[1]) + .build()); + + try { + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + bulletinBoardServer.readMessages(filterListBuilder.build(), new MessageOutputStream(outputStream)); + + MessageInputStream inputStream = + MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream( + outputStream.toByteArray()), + BulletinBoardMessage.class); + + messages = inputStream.asList(); + + } catch (CommunicationException e) { + System.err.println("Failed retrieving multi-signature message from DB: " + e.getMessage()); + fail("Failed retrieving multi-signature message from DB: " + e.getMessage()); + return; + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | IOException e) { + System.err.println("Falied to read from stream while retrieving multi-signature message: " + e.getMessage()); + fail("Falied to read from stream while retrieving multi-signature message: " + e.getMessage()); + return; + } + + assertThat(messages.size(), is(MESSAGE_NUM / 4)); + + for (BulletinBoardMessage message : messages) { + assertThat(message.getEntryNum() % 4, is((long) 1)); + } + + long end = threadBean.getCurrentThreadCpuTime(); + System.err.println("Finished testing multiple tags and signatures"); + System.err.println("Time of operation: " + (end - start)); + + } + + + private void postAsBatch(BulletinBoardMessage message, int chunkSize, boolean close) throws CommunicationException { + + List batchChunks = BulletinBoardUtils.breakToBatch(message, chunkSize); + BeginBatchMessage beginBatchMessage = BulletinBoardUtils.generateBeginBatchMessage(message); + + BoolValue result; + + // Begin batch + + Int64Value batchId = bulletinBoardServer.beginBatch(beginBatchMessage); + + assertThat("Was not able to open batch", batchId.getValue() != -1); + + // Post data + + BatchMessage batchMessage = BatchMessage.getDefaultInstance(); + + for (int i = 0 ; i < batchChunks.size() ; i++){ + + batchMessage = BatchMessage.newBuilder() + .setBatchId(batchId.getValue()) + .setSerialNum(i) + .setData(batchChunks.get(i)) + .build(); + + result = bulletinBoardServer.postBatchMessage(batchMessage); + + assertThat("Was not able to post batch message", result.getValue(), is(true)); + + } + + // Close batch + if (close) { + + CloseBatchMessage closeBatchMessage = BulletinBoardUtils.generateCloseBatchMessage(batchId, batchChunks.size(), message); + + result = bulletinBoardServer.closeBatch(closeBatchMessage); + + assertThat("Was not able to close batch", result.getValue(), is(true)); + + } + + } + + /** + * Posts a complete batch message + * @throws CommunicationException + */ + public void testPostBatch() throws CommunicationException, SignatureException { + + // Create data + final int BATCH_ID = 200; + final int DATA_SIZE = 10000; + final int CHUNK_SIZE = 100; + final int TAG_NUMBER = 10; + + Timestamp timestamp = Timestamp.newBuilder() + .setSeconds(5235000) + .setNanos(32541) + .build(); + + BulletinBoardMessage batch = bulletinBoardMessageGenerator.generateRandomMessage(signers, timestamp, DATA_SIZE, TAG_NUMBER); + + // Post batch + + postAsBatch(batch, CHUNK_SIZE, true); + + // Update locally stored batches + batches.add(batch); + + } + + public void testReadBatch() throws CommunicationException { + + for (BulletinBoardMessage message : batches) { + + try { + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + digest.update(message); + + MessageID msgId = digest.digestAsMessageID(); + + MessageFilterList messageFilterList = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.MSG_ID) + .setId(msgId.getID()) + .build()) + .build(); + + bulletinBoardServer.readMessages(messageFilterList, new MessageOutputStream(outputStream)); + + MessageInputStream messageInputStream = + MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream( + outputStream.toByteArray()), + BulletinBoardMessage.class); + + List messageList = messageInputStream.asList(); + + assertThat("No stub found for message ID " + msgId.getID().toStringUtf8(), messageList.size() == 1); + + BulletinBoardMessage stub = messageList.get(0); + + BatchQuery batchQuery = + BatchQuery.newBuilder() + .setMsgID(msgId) + .setStartPosition(0) + .build(); + + bulletinBoardServer.readBatch(batchQuery, new MessageOutputStream(outputStream)); + + MessageInputStream batchInputStream = + MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream( + outputStream.toByteArray()), + BatchChunk.class); + + List batchChunkList = batchInputStream.asList(); + + BulletinBoardMessage retrievedMessage = BulletinBoardUtils.gatherBatch(stub, batchChunkList); + + assertThat("Non-matching batch data for batch " + msgId.getID().toStringUtf8(), + comparator.compare(message, retrievedMessage) == 0); + + } catch (IOException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + assertThat("Error reading batch data list from input stream", false); + } + + } + + } + + public void testSyncQuery() + throws SignatureException, CommunicationException, IOException,NoSuchMethodException, IllegalAccessException, InvocationTargetException { + + Checksum checksum = new SimpleChecksum(); + + Timestamp timestamp = Timestamp.newBuilder() + .setSeconds(1) + .setNanos(0) + .build(); + + BulletinBoardMessage newMessage = bulletinBoardMessageGenerator.generateRandomMessage(signers, timestamp, 10, 10); + + BoolValue result = bulletinBoardServer.postMessage(newMessage); + assertThat("Failed to post message to BB Server", result.getValue(), is(true)); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + digest.update(newMessage.getMsg()); + + ByteString messageID = ByteString.copyFrom(digest.digest()); + + MessageFilterList filterList = MessageFilterList.newBuilder() + .addFilter(MessageFilter.newBuilder() + .setType(FilterType.MSG_ID) + .setId(messageID) + .build()) + .build(); + + bulletinBoardServer.readMessages(filterList, new MessageOutputStream(outputStream)); + + MessageInputStream inputStream = + MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream( + outputStream.toByteArray()), + BulletinBoardMessage.class); + + long lastEntry = inputStream.asList().get(0).getEntryNum(); + + SyncQuery syncQuery = SyncQuery.newBuilder() + .setFilterList(MessageFilterList.getDefaultInstance()) + .addQuery(SingleSyncQuery.newBuilder() + .setChecksum(2) + .setTimeOfSync(Timestamp.newBuilder() + .setSeconds(2) + .setNanos(0) + .build()) + .build()) + .build(); + + SyncQueryResponse queryResponse = bulletinBoardServer.querySync(syncQuery); + + assertThat("Sync query replies with positive sync when no sync was expected", queryResponse.getLastEntryNum(), is(equalTo(-1l))); + + syncQuery = SyncQuery.newBuilder() + .setFilterList(MessageFilterList.getDefaultInstance()) + .addQuery(SingleSyncQuery.newBuilder() + .setChecksum(checksum.getChecksum(messageID)) + .setTimeOfSync(timestamp) + .build()) + .build(); + + queryResponse = bulletinBoardServer.querySync(syncQuery); + + assertThat("Sync query reply contained wrong last entry number", lastEntry, is(equalTo(queryResponse.getLastEntryNum()))); + + assertThat("Sync query reply contained wrong timestamp", timestamp, is(equalTo(queryResponse.getLastTimeOfSync()))); + + } + + public void close(){ + signers[0].clearSigningKey(); + signers[1].clearSigningKey(); + try { + bulletinBoardServer.close(); + } catch (CommunicationException e) { + System.err.println("Error closing server " + e.getMessage()); + fail("Error closing server " + e.getMessage()); + } + } +} diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/H2BulletinBoardServerTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/H2BulletinBoardServerTest.java index a55cc74..def0b41 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/H2BulletinBoardServerTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/H2BulletinBoardServerTest.java @@ -1,165 +1,154 @@ -package meerkat.bulletinboard; - -import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; -import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider; -import meerkat.bulletinboard.sqlserver.H2QueryProvider; -import meerkat.comm.CommunicationException; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.Result; - -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadMXBean; -import java.sql.*; -import java.util.List; - -import static org.junit.Assert.fail; - -/** - * Created by Arbel Deutsch Peled on 07-Dec-15. - */ -public class H2BulletinBoardServerTest { - - private final String dbName = "meerkatTest"; - - private GenericBulletinBoardServerTest serverTest; - - private SQLQueryProvider queryProvider; - - private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests - - @Before - public void init(){ - - System.err.println("Starting to initialize H2BulletinBoardServerTest"); - long start = threadBean.getCurrentThreadCpuTime(); - - queryProvider = new H2QueryProvider(dbName); - - try { - - Connection conn = queryProvider.getDataSource().getConnection(); - Statement stmt = conn.createStatement(); - - List deletionQueries = queryProvider.getSchemaDeletionCommands(); - - for (String deletionQuery : deletionQueries) { - stmt.execute(deletionQuery); - } - - } catch (SQLException e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(queryProvider); - try { - bulletinBoardServer.init(""); - - } catch (CommunicationException e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - return; - } - - serverTest = new GenericBulletinBoardServerTest(); - try { - serverTest.init(bulletinBoardServer); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - long end = threadBean.getCurrentThreadCpuTime(); - System.err.println("Finished initializing H2BulletinBoardServerTest"); - System.err.println("Time of operation: " + (end - start)); - } - - @Test - public void bulkTest() { - System.err.println("Starting bulkTest of H2BulletinBoardServerTest"); - long start = threadBean.getCurrentThreadCpuTime(); - - try { - serverTest.testInsert(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - try{ - serverTest.testSimpleTagAndSignature(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - try{ - serverTest.testEnhancedTagsAndSignatures(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - long end = threadBean.getCurrentThreadCpuTime(); - System.err.println("Finished bulkTest of H2BulletinBoardServerTest"); - System.err.println("Time of operation: " + (end - start)); - } - - @Test - public void testBatchPostAfterClose() { - try{ - serverTest.testBatchPostAfterClose(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - } - - @Test - public void testBatch() { - - final int BATCH_NUM = 20; - - try{ - for (int i = 0 ; i < BATCH_NUM ; i++) { - serverTest.testPostBatch(); - } - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - try{ - serverTest.testReadBatch(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - } - - @Test - public void testSyncQuery() { - try { - serverTest.testSyncQuery(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - } - - @After - public void close() { - System.err.println("Starting to close H2BulletinBoardServerTest"); - long start = threadBean.getCurrentThreadCpuTime(); - - serverTest.close(); - - long end = threadBean.getCurrentThreadCpuTime(); - System.err.println("Finished closing H2BulletinBoardServerTest"); - System.err.println("Time of operation: " + (end - start)); - } - -} +package meerkat.bulletinboard; + +import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; +import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider; +import meerkat.bulletinboard.sqlserver.H2QueryProvider; +import meerkat.comm.CommunicationException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.sql.*; +import java.util.List; + +import static org.junit.Assert.fail; + +/** + * Created by Arbel Deutsch Peled on 07-Dec-15. + */ +public class H2BulletinBoardServerTest { + + private final String dbName = "meerkatTest"; + + private GenericBulletinBoardServerTest serverTest; + + private SQLQueryProvider queryProvider; + + private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests + + @Before + public void init(){ + + System.err.println("Starting to initialize H2BulletinBoardServerTest"); + long start = threadBean.getCurrentThreadCpuTime(); + + queryProvider = new H2QueryProvider(dbName); + + try { + + Connection conn = queryProvider.getDataSource().getConnection(); + Statement stmt = conn.createStatement(); + + List deletionQueries = queryProvider.getSchemaDeletionCommands(); + + for (String deletionQuery : deletionQueries) { + stmt.execute(deletionQuery); + } + + } catch (SQLException e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(queryProvider); + try { + bulletinBoardServer.init(); + + } catch (CommunicationException e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + return; + } + + serverTest = new GenericBulletinBoardServerTest(); + try { + serverTest.init(bulletinBoardServer); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + long end = threadBean.getCurrentThreadCpuTime(); + System.err.println("Finished initializing H2BulletinBoardServerTest"); + System.err.println("Time of operation: " + (end - start)); + } + + @Test + public void bulkTest() { + System.err.println("Starting bulkTest of H2BulletinBoardServerTest"); + long start = threadBean.getCurrentThreadCpuTime(); + + try { + serverTest.testInsert(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + try{ + serverTest.testSimpleTagAndSignature(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + try{ + serverTest.testEnhancedTagsAndSignatures(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + long end = threadBean.getCurrentThreadCpuTime(); + System.err.println("Finished bulkTest of H2BulletinBoardServerTest"); + System.err.println("Time of operation: " + (end - start)); + } + + @Test + public void testBatch() { + + final int BATCH_NUM = 20; + + try{ + for (int i = 0 ; i < BATCH_NUM ; i++) { + serverTest.testPostBatch(); + } + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + try{ + serverTest.testReadBatch(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + } + + @Test + public void testSyncQuery() { + try { + serverTest.testSyncQuery(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + } + + @After + public void close() { + System.err.println("Starting to close H2BulletinBoardServerTest"); + long start = threadBean.getCurrentThreadCpuTime(); + + serverTest.close(); + + long end = threadBean.getCurrentThreadCpuTime(); + System.err.println("Finished closing H2BulletinBoardServerTest"); + System.err.println("Time of operation: " + (end - start)); + } + +} diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/HelloProtoIntegrationTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/HelloProtoIntegrationTest.java index a0c3653..c7b16df 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/HelloProtoIntegrationTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/HelloProtoIntegrationTest.java @@ -1,42 +1,42 @@ -package meerkat.bulletinboard; - -import meerkat.protobuf.BulletinBoardAPI.*; -import meerkat.rest.Constants; -import meerkat.rest.ProtobufMessageBodyReader; -import meerkat.rest.ProtobufMessageBodyWriter; -import org.junit.Test; - -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.WebTarget; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - -/** - * Created by talm on 10/11/15. - */ -public class HelloProtoIntegrationTest { - private static String PROP_GETTY_URL = "gretty.httpBaseURI"; - private static String DEFAULT_BASE_URL = "http://localhost:8081/"; - private static String BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL); - private static String HELLO_URL = "proto"; - - @Test - public void testHello() throws Exception { - Client client = ClientBuilder.newClient(); - client.register(ProtobufMessageBodyReader.class); - client.register(ProtobufMessageBodyWriter.class); - - WebTarget webTarget = client.target(BASE_URL).path(HELLO_URL); - BulletinBoardMessage response = webTarget.request(Constants.MEDIATYPE_PROTOBUF) - .get(BulletinBoardMessage.class); - - System.out.println(response.getMsg().getData()); - - assertThat(response.getMsg().getData().toStringUtf8(), is("Hello World!")); - assertThat(response.getMsg().getTagCount(), is(2)); - assertThat(response.getMsg().getTag(0), is("Greetings")); - assertThat(response.getMsg().getTag(1), is("FirstPrograms")); - } -} +package meerkat.bulletinboard; + +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.rest.Constants; +import meerkat.rest.ProtobufMessageBodyReader; +import meerkat.rest.ProtobufMessageBodyWriter; +import org.junit.Test; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Created by talm on 10/11/15. + */ +public class HelloProtoIntegrationTest { + private static String PROP_GETTY_URL = "gretty.httpBaseURI"; + private static String DEFAULT_BASE_URL = "http://localhost:8081/"; + private static String BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL); + private static String HELLO_URL = "proto"; + + @Test + public void testHello() throws Exception { + Client client = ClientBuilder.newClient(); + client.register(ProtobufMessageBodyReader.class); + client.register(ProtobufMessageBodyWriter.class); + + WebTarget webTarget = client.target(BASE_URL).path(HELLO_URL); + BulletinBoardMessage response = webTarget.request(Constants.MEDIATYPE_PROTOBUF) + .get(BulletinBoardMessage.class); + + System.out.println(response.getMsg().getData()); + + assertThat(response.getMsg().getData().toStringUtf8(), is("Hello World!")); + assertThat(response.getMsg().getTagCount(), is(2)); + assertThat(response.getMsg().getTag(0), is("Greetings")); + assertThat(response.getMsg().getTag(1), is("FirstPrograms")); + } +} diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java index 48f8d89..abc0fc6 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java @@ -1,172 +1,158 @@ -package meerkat.bulletinboard; - -import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; -import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider; -import meerkat.bulletinboard.sqlserver.MySQLQueryProvider; -import meerkat.comm.CommunicationException; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadMXBean; -import java.lang.reflect.InvocationTargetException; -import java.security.SignatureException; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.List; - -import static org.junit.Assert.fail; - -/** - * Created by Arbel Deutsch Peled on 07-Dec-15. - */ -public class MySQLBulletinBoardServerTest { - - private final String dbAddress = "localhost"; - private final int dbPort = 3306; - private final String dbName = "meerkat"; - private final String username = "arbel"; - private final String password = "mypass"; - - private GenericBulletinBoardServerTest serverTest; - - private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests - - @Before - public void init(){ - - System.err.println("Starting to initialize MySQLBulletinBoardServerTest"); - long start = threadBean.getCurrentThreadCpuTime(); - - SQLQueryProvider queryProvider = new MySQLQueryProvider(dbAddress,dbPort,dbName,username,password); - - try { - - Connection conn = queryProvider.getDataSource().getConnection(); - Statement stmt = conn.createStatement(); - - List deletionQueries = queryProvider.getSchemaDeletionCommands(); - - for (String deletionQuery : deletionQueries) { - stmt.execute(deletionQuery); - } - - } catch (SQLException e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(queryProvider); - try { - bulletinBoardServer.init(""); - - } catch (CommunicationException e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - return; - } - - serverTest = new GenericBulletinBoardServerTest(); - try { - serverTest.init(bulletinBoardServer); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - long end = threadBean.getCurrentThreadCpuTime(); - System.err.println("Finished initializing MySQLBulletinBoardServerTest"); - System.err.println("Time of operation: " + (end - start)); - } - - @Test - public void bulkTest() { - System.err.println("Starting bulkTest of MySQLBulletinBoardServerTest"); - long start = threadBean.getCurrentThreadCpuTime(); - - try { - serverTest.testInsert(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - try{ - serverTest.testSimpleTagAndSignature(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - try{ - serverTest.testEnhancedTagsAndSignatures(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - long end = threadBean.getCurrentThreadCpuTime(); - System.err.println("Finished bulkTest of MySQLBulletinBoardServerTest"); - System.err.println("Time of operation: " + (end - start)); - } - - @Test - public void testBatchPostAfterClose() { - try{ - serverTest.testBatchPostAfterClose(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - } - - @Test - public void testBatch() { - - final int BATCH_NUM = 20; - - try{ - for (int i = 0 ; i < BATCH_NUM ; i++) { - serverTest.testPostBatch(); - } - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - try{ - serverTest.testReadBatch(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - } - - @Test - public void testSyncQuery() { - try { - serverTest.testSyncQuery(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - } - - @After - public void close() { - System.err.println("Starting to close MySQLBulletinBoardServerTest"); - long start = threadBean.getCurrentThreadCpuTime(); - - serverTest.close(); - - long end = threadBean.getCurrentThreadCpuTime(); - System.err.println("Finished closing MySQLBulletinBoardServerTest"); - System.err.println("Time of operation: " + (end - start)); - } - -} +package meerkat.bulletinboard; + +import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; +import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider; +import meerkat.bulletinboard.sqlserver.MySQLQueryProvider; +import meerkat.comm.CommunicationException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; + +import static org.junit.Assert.fail; + +/** + * Created by Arbel Deutsch Peled on 07-Dec-15. + */ +public class MySQLBulletinBoardServerTest { + + private final String dbAddress = "localhost"; + private final int dbPort = 3306; + private final String dbName = "meerkat"; + private final String username = "arbel"; + private final String password = "mypass"; + + private GenericBulletinBoardServerTest serverTest; + + private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests + + @Before + public void init(){ + + System.err.println("Starting to initialize MySQLBulletinBoardServerTest"); + long start = threadBean.getCurrentThreadCpuTime(); + + SQLQueryProvider queryProvider = new MySQLQueryProvider(dbAddress,dbPort,dbName,username,password); + + try { + + Connection conn = queryProvider.getDataSource().getConnection(); + Statement stmt = conn.createStatement(); + + List deletionQueries = queryProvider.getSchemaDeletionCommands(); + + for (String deletionQuery : deletionQueries) { + stmt.execute(deletionQuery); + } + + } catch (SQLException e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(queryProvider); + try { + bulletinBoardServer.init(); + + } catch (CommunicationException e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + return; + } + + serverTest = new GenericBulletinBoardServerTest(); + try { + serverTest.init(bulletinBoardServer); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + long end = threadBean.getCurrentThreadCpuTime(); + System.err.println("Finished initializing MySQLBulletinBoardServerTest"); + System.err.println("Time of operation: " + (end - start)); + } + + @Test + public void bulkTest() { + System.err.println("Starting bulkTest of MySQLBulletinBoardServerTest"); + long start = threadBean.getCurrentThreadCpuTime(); + + try { + serverTest.testInsert(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + try{ + serverTest.testSimpleTagAndSignature(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + try{ + serverTest.testEnhancedTagsAndSignatures(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + long end = threadBean.getCurrentThreadCpuTime(); + System.err.println("Finished bulkTest of MySQLBulletinBoardServerTest"); + System.err.println("Time of operation: " + (end - start)); + } + + @Test + public void testBatch() { + + final int BATCH_NUM = 20; + + try{ + for (int i = 0 ; i < BATCH_NUM ; i++) { + serverTest.testPostBatch(); + } + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + try{ + serverTest.testReadBatch(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + } + + @Test + public void testSyncQuery() { + try { + serverTest.testSyncQuery(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + } + + @After + public void close() { + System.err.println("Starting to close MySQLBulletinBoardServerTest"); + long start = threadBean.getCurrentThreadCpuTime(); + + serverTest.close(); + + long end = threadBean.getCurrentThreadCpuTime(); + System.err.println("Finished closing MySQLBulletinBoardServerTest"); + System.err.println("Time of operation: " + (end - start)); + } + +} diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteBulletinBoardServerTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteBulletinBoardServerTest.java index cbbd6f9..18278b3 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteBulletinBoardServerTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteBulletinBoardServerTest.java @@ -1,106 +1,129 @@ -package meerkat.bulletinboard; - -import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; -import meerkat.bulletinboard.sqlserver.SQLiteQueryProvider; -import meerkat.comm.CommunicationException; -import meerkat.protobuf.*; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadMXBean; -import java.security.*; -import java.security.cert.CertificateException; - -import static org.junit.Assert.fail; - -/** - * Created by Arbel Deutsch Peled on 07-Dec-15. - */ -public class SQLiteBulletinBoardServerTest{ - - private String testFilename = "SQLiteDBTest.db"; - - private GenericBulletinBoardServerTest serverTest; - - private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests - - @Before - public void init(){ - - System.err.println("Starting to initialize SQLiteBulletinBoardServerTest"); - long start = threadBean.getCurrentThreadCpuTime(); - - File old = new File(testFilename); - old.delete(); - - BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(new SQLiteQueryProvider(testFilename)); - try { - bulletinBoardServer.init(""); - - } catch (CommunicationException e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - return; - } - - serverTest = new GenericBulletinBoardServerTest(); - try { - serverTest.init(bulletinBoardServer); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - long end = threadBean.getCurrentThreadCpuTime(); - System.err.println("Finished initializing SQLiteBulletinBoardServerTest"); - System.err.println("Time of operation: " + (end - start)); - } - - @Test - public void bulkTest() { - System.err.println("Starting bulkTest of SQLiteBulletinBoardServerTest"); - long start = threadBean.getCurrentThreadCpuTime(); - - try { - serverTest.testInsert(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - try{ - serverTest.testSimpleTagAndSignature(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - try{ - serverTest.testEnhancedTagsAndSignatures(); - } catch (Exception e) { - System.err.println(e.getMessage()); - fail(e.getMessage()); - } - - long end = threadBean.getCurrentThreadCpuTime(); - System.err.println("Finished bulkTest of SQLiteBulletinBoardServerTest"); - System.err.println("Time of operation: " + (end - start)); - } - - @After - public void close() { - System.err.println("Starting to close SQLiteBulletinBoardServerTest"); - long start = threadBean.getCurrentThreadCpuTime(); - - serverTest.close(); - - long end = threadBean.getCurrentThreadCpuTime(); - System.err.println("Finished closing SQLiteBulletinBoardServerTest"); - System.err.println("Time of operation: " + (end - start)); - } - -} +package meerkat.bulletinboard; + +import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; +import meerkat.bulletinboard.sqlserver.SQLiteQueryProvider; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.*; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.security.*; +import java.security.cert.CertificateException; + +import static org.junit.Assert.fail; + +/** + * Created by Arbel Deutsch Peled on 07-Dec-15. + */ +public class SQLiteBulletinBoardServerTest{ + + private String testFilename = "SQLiteDBTest.db"; + + private GenericBulletinBoardServerTest serverTest; + + private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests + + @Before + public void init(){ + + System.err.println("Starting to initialize SQLiteBulletinBoardServerTest"); + long start = threadBean.getCurrentThreadCpuTime(); + + File old = new File(testFilename); + old.delete(); + + BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(new SQLiteQueryProvider(testFilename)); + try { + bulletinBoardServer.init(); + + } catch (CommunicationException e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + return; + } + + serverTest = new GenericBulletinBoardServerTest(); + try { + serverTest.init(bulletinBoardServer); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + long end = threadBean.getCurrentThreadCpuTime(); + System.err.println("Finished initializing SQLiteBulletinBoardServerTest"); + System.err.println("Time of operation: " + (end - start)); + } + +// @Test + public void bulkTest() { + System.err.println("Starting bulkTest of SQLiteBulletinBoardServerTest"); + long start = threadBean.getCurrentThreadCpuTime(); + + try { + serverTest.testInsert(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + try{ + serverTest.testSimpleTagAndSignature(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + try{ + serverTest.testEnhancedTagsAndSignatures(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + long end = threadBean.getCurrentThreadCpuTime(); + System.err.println("Finished bulkTest of SQLiteBulletinBoardServerTest"); + System.err.println("Time of operation: " + (end - start)); + } + +// @Test + public void testBatch() { + + final int BATCH_NUM = 20; + + try{ + for (int i = 0 ; i < BATCH_NUM ; i++) { + serverTest.testPostBatch(); + } + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + try{ + serverTest.testReadBatch(); + } catch (Exception e) { + System.err.println(e.getMessage()); + fail(e.getMessage()); + } + + } + + @After + public void close() { + System.err.println("Starting to close SQLiteBulletinBoardServerTest"); + long start = threadBean.getCurrentThreadCpuTime(); + + serverTest.close(); + + long end = threadBean.getCurrentThreadCpuTime(); + System.err.println("Finished closing SQLiteBulletinBoardServerTest"); + System.err.println("Time of operation: " + (end - start)); + } + +} diff --git a/distributed-key-generation/build.gradle b/distributed-key-generation/build.gradle index 8f0a397..c90a15a 100644 --- a/distributed-key-generation/build.gradle +++ b/distributed-key-generation/build.gradle @@ -1,223 +1,223 @@ - -plugins { - id "us.kirchmeier.capsule" version "1.0.1" - id 'com.google.protobuf' version '0.7.0' -} - -apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'idea' - -apply plugin: 'maven-publish' - -// Uncomment the lines below to define an application -// (this will also allow you to build a "fatCapsule" which includes -// the entire application, including all dependencies in a single jar) -//apply plugin: 'application' -//mainClassName='your.main.ApplicationClass' - -// Is this a snapshot version? -ext { isSnapshot = false } - -ext { - groupId = 'org.factcenter.meerkat' - nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" - - // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) - // Should be set in ${HOME}/.gradle/gradle.properties - nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" - nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" -} - -description = "TODO: Add a description" - -// Your project version -version = "0.0" - -version += "${isSnapshot ? '-SNAPSHOT' : ''}" - - -dependencies { - // Meerkat common - compile project(':meerkat-common') - - // Logging - compile 'org.slf4j:slf4j-api:1.7.7' - runtime 'ch.qos.logback:logback-classic:1.1.2' - runtime 'ch.qos.logback:logback-core:1.1.2' - - // Google protobufs - compile 'com.google.protobuf:protobuf-java:3.+' - - // Depend on test resources from meerkat-common - testCompile project(path: ':meerkat-common', configuration: 'testOutput') - - testCompile 'junit:junit:4.+' - - runtime 'org.codehaus.groovy:groovy:2.4.+' -} - - -/*==== You probably don't have to edit below this line =======*/ - -// Setup test configuration that can appear as a dependency in -// other subprojects -configurations { - testOutput.extendsFrom (testCompile) -} - -task testJar(type: Jar, dependsOn: testClasses) { - classifier = 'tests' - from sourceSets.test.output -} - -artifacts { - testOutput testJar -} - -// The run task added by the application plugin -// is also of type JavaExec. -tasks.withType(JavaExec) { - // Assign all Java system properties from - // the command line to the JavaExec task. - systemProperties System.properties -} - - -protobuf { - // Configure the protoc executable - protoc { - // Download from repositories - artifact = 'com.google.protobuf:protoc:3.+' - } -} - - -idea { - module { - project.sourceSets.each { sourceSet -> - - def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" - - println "Adding $srcDir" - // add protobuf generated sources to generated source dir. - if ("test".equals(sourceSet.name)) { - testSourceDirs += file(srcDir) - } else { - sourceDirs += file(srcDir) - } - generatedSourceDirs += file(srcDir) - - } - - // Don't exclude build directory - excludeDirs -= file(buildDir) - } -} - - -/*=================================== - * "Fat" Build targets - *===================================*/ - - -if (project.hasProperty('mainClassName') && (mainClassName != null)) { - - task mavenCapsule(type: MavenCapsule) { - description = "Generate a capsule jar that automatically downloads and caches dependencies when run." - applicationClass mainClassName - destinationDir = buildDir - } - - task fatCapsule(type: FatCapsule) { - description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class" - - destinationDir = buildDir - - def fatMain = hasProperty('fatmain') ? fatmain : mainClassName - - applicationClass fatMain - - def testJar = hasProperty('test') - - if (hasProperty('fatmain')) { - appendix = "fat-${fatMain}" - } else { - appendix = "fat" - } - - if (testJar) { - from sourceSets.test.output - } - } -} - - -/*=================================== - * Repositories - *===================================*/ - -repositories { - - // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) - maven { - url nexusRepository - - if (isSnapshot) { - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } - - // Use local maven repository - mavenLocal() - - // Use 'maven central' for other dependencies. - mavenCentral() -} - -task "info" << { - println "Project: ${project.name}" -println "Description: ${project.description}" - println "--------------------------" - println "GroupId: $groupId" - println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" - println "" -} -info.description 'Print some information about project parameters' - - -/*=================================== - * Publishing - *===================================*/ - -publishing { - publications { - mavenJava(MavenPublication) { - groupId project.groupId - pom.withXml { - asNode().appendNode('description', project.description) - } - from project.components.java - - } - } - repositories { - maven { - url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } -} - - - + +plugins { + id "us.kirchmeier.capsule" version "1.0.1" + id 'com.google.protobuf' version '0.7.0' +} + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'idea' + +apply plugin: 'maven-publish' + +// Uncomment the lines below to define an application +// (this will also allow you to build a "fatCapsule" which includes +// the entire application, including all dependencies in a single jar) +//apply plugin: 'application' +//mainClassName='your.main.ApplicationClass' + +// Is this a snapshot version? +ext { isSnapshot = false } + +ext { + groupId = 'org.factcenter.meerkat' + nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" + + // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) + // Should be set in ${HOME}/.gradle/gradle.properties + nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" + nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" +} + +description = "TODO: Add a description" + +// Your project version +version = "0.0" + +version += "${isSnapshot ? '-SNAPSHOT' : ''}" + + +dependencies { + // Meerkat common + compile project(':meerkat-common') + + // Logging + compile 'org.slf4j:slf4j-api:1.7.7' + runtime 'ch.qos.logback:logback-classic:1.1.2' + runtime 'ch.qos.logback:logback-core:1.1.2' + + // Google protobufs + compile 'com.google.protobuf:protobuf-java:3.+' + + // Depend on test resources from meerkat-common + testCompile project(path: ':meerkat-common', configuration: 'testOutput') + + testCompile 'junit:junit:4.+' + + runtime 'org.codehaus.groovy:groovy:2.4.+' +} + + +/*==== You probably don't have to edit below this line =======*/ + +// Setup test configuration that can appear as a dependency in +// other subprojects +configurations { + testOutput.extendsFrom (testCompile) +} + +task testJar(type: Jar, dependsOn: testClasses) { + classifier = 'tests' + from sourceSets.test.output +} + +artifacts { + testOutput testJar +} + +// The run task added by the application plugin +// is also of type JavaExec. +tasks.withType(JavaExec) { + // Assign all Java system properties from + // the command line to the JavaExec task. + systemProperties System.properties +} + + +protobuf { + // Configure the protoc executable + protoc { + // Download from repositories + artifact = 'com.google.protobuf:protoc:3.+' + } +} + + +idea { + module { + project.sourceSets.each { sourceSet -> + + def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" + + println "Adding $srcDir" + // add protobuf generated sources to generated source dir. + if ("test".equals(sourceSet.name)) { + testSourceDirs += file(srcDir) + } else { + sourceDirs += file(srcDir) + } + generatedSourceDirs += file(srcDir) + + } + + // Don't exclude build directory + excludeDirs -= file(buildDir) + } +} + + +/*=================================== + * "Fat" Build targets + *===================================*/ + + +if (project.hasProperty('mainClassName') && (mainClassName != null)) { + + task mavenCapsule(type: MavenCapsule) { + description = "Generate a capsule jar that automatically downloads and caches dependencies when run." + applicationClass mainClassName + destinationDir = buildDir + } + + task fatCapsule(type: FatCapsule) { + description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class" + + destinationDir = buildDir + + def fatMain = hasProperty('fatmain') ? fatmain : mainClassName + + applicationClass fatMain + + def testJar = hasProperty('test') + + if (hasProperty('fatmain')) { + appendix = "fat-${fatMain}" + } else { + appendix = "fat" + } + + if (testJar) { + from sourceSets.test.output + } + } +} + + +/*=================================== + * Repositories + *===================================*/ + +repositories { + + // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) + maven { + url nexusRepository + + if (isSnapshot) { + credentials { username + password + + username nexusUser + password nexusPassword + } + } + } + + // Use local maven repository + mavenLocal() + + // Use 'maven central' for other dependencies. + mavenCentral() +} + +task "info" << { + println "Project: ${project.name}" +println "Description: ${project.description}" + println "--------------------------" + println "GroupId: $groupId" + println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" + println "" +} +info.description 'Print some information about project parameters' + + +/*=================================== + * Publishing + *===================================*/ + +publishing { + publications { + mavenJava(MavenPublication) { + groupId project.groupId + pom.withXml { + asNode().appendNode('description', project.description) + } + from project.components.java + + } + } + repositories { + maven { + url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + credentials { username + password + + username nexusUser + password nexusPassword + } + } + } +} + + + diff --git a/distributed-key-generation/src/main/proto/meerkat/DKG.proto b/distributed-key-generation/src/main/proto/meerkat/DKG.proto index 7d43a67..3457fbe 100644 --- a/distributed-key-generation/src/main/proto/meerkat/DKG.proto +++ b/distributed-key-generation/src/main/proto/meerkat/DKG.proto @@ -1,46 +1,46 @@ -syntax = "proto3"; - -package meerkat; - -option java_package = "meerkat.protobuf"; - -message Payload { - enum Type { - SHARE = 0; - COMMITMENT = 1; - COMPLAINT = 2; - DONE = 3; - ANSWER = 4; - YCOMMITMENT = 5; - YCOMPLAINT = 6; - YANSWER = 7; - ABORT = 8; - } - - - // Type of message in protocol - Type type = 1; - - oneof payload_data { - IDMessage id = 5; - ShareMessage share = 6; - CommitmentMessage commitment = 7; - } -} - -message IDMessage { - int32 id = 1; -} - -message ShareMessage { - int32 i = 1; - int32 j = 2; - bytes share = 3; - // For double shares (used in GJKR protocol) - bytes share_t = 4; -} - -message CommitmentMessage { - int32 k = 1; - bytes commitment = 2; -} +syntax = "proto3"; + +package meerkat; + +option java_package = "meerkat.protobuf"; + +message Payload { + enum Type { + SHARE = 0; + COMMITMENT = 1; + COMPLAINT = 2; + DONE = 3; + ANSWER = 4; + YCOMMITMENT = 5; + YCOMPLAINT = 6; + YANSWER = 7; + ABORT = 8; + } + + + // Type of message in protocol + Type type = 1; + + oneof payload_data { + IDMessage id = 5; + ShareMessage share = 6; + CommitmentMessage commitment = 7; + } +} + +message IDMessage { + int32 id = 1; +} + +message ShareMessage { + int32 i = 1; + int32 j = 2; + bytes share = 3; + // For double shares (used in GJKR protocol) + bytes share_t = 4; +} + +message CommitmentMessage { + int32 k = 1; + bytes commitment = 2; +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 9411448..30d399d 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 48f7220..864f396 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ -#Fri Jan 29 21:00:29 IST 2016 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.9-bin.zip +#Tue Aug 05 03:26:05 IDT 2014 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.9-all.zip +distributionSha256Sum=4647967f8de78d6d6d8093cdac50f368f8c2b8038f41a5afe1c3bce4c69219a9 diff --git a/gradlew b/gradlew index 9d82f78..91a7e26 100755 --- a/gradlew +++ b/gradlew @@ -42,6 +42,11 @@ case "`uname`" in ;; esac +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" @@ -56,9 +61,9 @@ while [ -h "$PRG" ] ; do fi done SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null +cd "`dirname \"$PRG\"`/" >&- APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null +cd "$SAVED" >&- CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -109,7 +114,6 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` diff --git a/meerkat-common/.gitignore b/meerkat-common/.gitignore index 3e2fcc7..ae3c172 100644 --- a/meerkat-common/.gitignore +++ b/meerkat-common/.gitignore @@ -1 +1 @@ -/bin/ +/bin/ diff --git a/meerkat-common/build.gradle b/meerkat-common/build.gradle index 33840bd..a9035b0 100644 --- a/meerkat-common/build.gradle +++ b/meerkat-common/build.gradle @@ -1,219 +1,219 @@ - -plugins { - id "us.kirchmeier.capsule" version "1.0.1" - id 'com.google.protobuf' version '0.7.0' -} - -apply plugin: 'java' -apply plugin: 'com.google.protobuf' -apply plugin: 'eclipse' -apply plugin: 'idea' - -apply plugin: 'application' - -apply plugin: 'maven-publish' - -mainClassName='Demo' - -// Is this a snapshot version? -ext { isSnapshot = false } - -ext { - groupId = 'org.factcenter.meerkat' - nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" - - // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) - // Should be set in ${HOME}/.gradle/gradle.properties - nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" - nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" -} - -description = "Meerkat Voting Common Library" - -// Your project version -version = "0.0" - -version += "${isSnapshot ? '-SNAPSHOT' : ''}" - - -dependencies { - // Logging - compile 'org.slf4j:slf4j-api:1.7.7' - compile 'javax.ws.rs:javax.ws.rs-api:2.0.+' - runtime 'ch.qos.logback:logback-classic:1.1.2' - runtime 'ch.qos.logback:logback-core:1.1.2' - - // Google protobufs - compile 'com.google.protobuf:protobuf-java:3.+' - - // ListeningExecutor - compile 'com.google.guava:guava:15.0' - - // Crypto - compile 'org.factcenter.qilin:qilin:1.2+' - compile 'org.bouncycastle:bcprov-jdk15on:1.53' - - testCompile 'junit:junit:4.+' - - runtime 'org.codehaus.groovy:groovy:2.4.+' -} - - -/*==== You probably don't have to edit below this line =======*/ - - -// Setup test configuration that can appear as a dependency in -// other subprojects -configurations { - testOutput.extendsFrom (testCompile) -} - -task testJar(type: Jar, dependsOn: testClasses) { - classifier = 'tests' - from sourceSets.test.output -} - -artifacts { - testOutput testJar -} - - - -// The run task added by the application plugin -// is also of type JavaExec. -tasks.withType(JavaExec) { - // Assign all Java system properties from - // the command line to the JavaExec task. - systemProperties System.properties -} - - -protobuf { - // Configure the protoc executable - protoc { - // Download from repositories - artifact = 'com.google.protobuf:protoc:3.+' - } -} - -idea { - module { - project.sourceSets.each { sourceSet -> - - def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" - - // add protobuf generated sources to generated source dir. - if ("test".equals(sourceSet.name)) { - testSourceDirs += file(srcDir) - } else { - sourceDirs += file(srcDir) - } - generatedSourceDirs += file(srcDir) - - } - - // Don't exclude build directory - excludeDirs -= file(buildDir) - } -} - -/*=================================== - * "Fat" Build targets - *===================================*/ - - -task mavenCapsule(type: MavenCapsule){ - description = "Generate a capsule jar that automatically downloads and caches dependencies when run." - applicationClass mainClassName - destinationDir = buildDir -} - -task fatCapsule(type: FatCapsule){ - description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class" - - destinationDir = buildDir - - def fatMain = hasProperty('fatmain') ? fatmain : mainClassName - - applicationClass fatMain - - def testJar = hasProperty('test') - - if (hasProperty('fatmain')) { - appendix = "fat-${fatMain}" - } else { - appendix = "fat" - } - - if (testJar) { - from sourceSets.test.output - } -} - -/*=================================== - * Repositories - *===================================*/ - -repositories { - - mavenLocal(); - - // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) - maven { - url nexusRepository - - if (isSnapshot) { - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } - - // Use 'maven central' for other dependencies. - mavenCentral() -} - -task "info" << { - println "Project: ${project.name}" -println "Description: ${project.description}" - println "--------------------------" - println "GroupId: $groupId" - println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" - println "" -} -info.description 'Print some information about project parameters' - - -/*=================================== - * Publishing - *===================================*/ - -publishing { - publications { - mavenJava(MavenPublication) { - groupId project.groupId - pom.withXml { - asNode().appendNode('description', project.description) - } - from project.components.java - - } - } - repositories { - maven { - url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } -} - - - + +plugins { + id "us.kirchmeier.capsule" version "1.0.1" + id 'com.google.protobuf' version '0.7.0' +} + +apply plugin: 'java' +apply plugin: 'com.google.protobuf' +apply plugin: 'eclipse' +apply plugin: 'idea' + +apply plugin: 'application' + +apply plugin: 'maven-publish' + +mainClassName='Demo' + +// Is this a snapshot version? +ext { isSnapshot = false } + +ext { + groupId = 'org.factcenter.meerkat' + nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" + + // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) + // Should be set in ${HOME}/.gradle/gradle.properties + nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" + nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" +} + +description = "Meerkat Voting Common Library" + +// Your project version +version = "0.0" + +version += "${isSnapshot ? '-SNAPSHOT' : ''}" + + +dependencies { + // Logging + compile 'org.slf4j:slf4j-api:1.7.7' + compile 'javax.ws.rs:javax.ws.rs-api:2.0.+' + runtime 'ch.qos.logback:logback-classic:1.1.2' + runtime 'ch.qos.logback:logback-core:1.1.2' + + // Google protobufs + compile 'com.google.protobuf:protobuf-java:3.+' + + // ListeningExecutor + compile 'com.google.guava:guava:15.0' + + // Crypto + compile 'org.factcenter.qilin:qilin:1.2+' + compile 'org.bouncycastle:bcprov-jdk15on:1.53' + + testCompile 'junit:junit:4.+' + + runtime 'org.codehaus.groovy:groovy:2.4.+' +} + + +/*==== You probably don't have to edit below this line =======*/ + + +// Setup test configuration that can appear as a dependency in +// other subprojects +configurations { + testOutput.extendsFrom (testCompile) +} + +task testJar(type: Jar, dependsOn: testClasses) { + classifier = 'tests' + from sourceSets.test.output +} + +artifacts { + testOutput testJar +} + + + +// The run task added by the application plugin +// is also of type JavaExec. +tasks.withType(JavaExec) { + // Assign all Java system properties from + // the command line to the JavaExec task. + systemProperties System.properties +} + + +protobuf { + // Configure the protoc executable + protoc { + // Download from repositories + artifact = 'com.google.protobuf:protoc:3.+' + } +} + +idea { + module { + project.sourceSets.each { sourceSet -> + + def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" + + // add protobuf generated sources to generated source dir. + if ("test".equals(sourceSet.name)) { + testSourceDirs += file(srcDir) + } else { + sourceDirs += file(srcDir) + } + generatedSourceDirs += file(srcDir) + + } + + // Don't exclude build directory + excludeDirs -= file(buildDir) + } +} + +/*=================================== + * "Fat" Build targets + *===================================*/ + + +task mavenCapsule(type: MavenCapsule){ + description = "Generate a capsule jar that automatically downloads and caches dependencies when run." + applicationClass mainClassName + destinationDir = buildDir +} + +task fatCapsule(type: FatCapsule){ + description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class" + + destinationDir = buildDir + + def fatMain = hasProperty('fatmain') ? fatmain : mainClassName + + applicationClass fatMain + + def testJar = hasProperty('test') + + if (hasProperty('fatmain')) { + appendix = "fat-${fatMain}" + } else { + appendix = "fat" + } + + if (testJar) { + from sourceSets.test.output + } +} + +/*=================================== + * Repositories + *===================================*/ + +repositories { + + mavenLocal(); + + // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) + maven { + url nexusRepository + + if (isSnapshot) { + credentials { username + password + + username nexusUser + password nexusPassword + } + } + } + + // Use 'maven central' for other dependencies. + mavenCentral() +} + +task "info" << { + println "Project: ${project.name}" +println "Description: ${project.description}" + println "--------------------------" + println "GroupId: $groupId" + println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" + println "" +} +info.description 'Print some information about project parameters' + + +/*=================================== + * Publishing + *===================================*/ + +publishing { + publications { + mavenJava(MavenPublication) { + groupId project.groupId + pom.withXml { + asNode().appendNode('description', project.description) + } + from project.components.java + + } + } + repositories { + maven { + url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + credentials { username + password + + username nexusUser + password nexusPassword + } + } + } +} + + + diff --git a/meerkat-common/src/main/java/Demo.java b/meerkat-common/src/main/java/Demo.java index 9f5f379..130adcc 100644 --- a/meerkat-common/src/main/java/Demo.java +++ b/meerkat-common/src/main/java/Demo.java @@ -1,29 +1,29 @@ -import com.google.protobuf.ByteString; -import static meerkat.protobuf.BulletinBoardAPI.*; -import java.io.IOException; - -/** - * Created by talm on 10/26/15. - */ -public class Demo { - public static void main(String args[]) { - System.out.println("Nothing to see yet"); - - BulletinBoardMessage msg; - - UnsignedBulletinBoardMessage msgContents = UnsignedBulletinBoardMessage.newBuilder() - .addTag("test") - .setData(ByteString.copyFromUtf8("some data")) - .build(); - - msg = BulletinBoardMessage.newBuilder() - .setMsg(msgContents) - .build(); - - try { - msg.writeTo(System.err); - } catch (IOException e) { - // Ignore - } - } -} +import com.google.protobuf.ByteString; +import static meerkat.protobuf.BulletinBoardAPI.*; +import java.io.IOException; + +/** + * Created by talm on 10/26/15. + */ +public class Demo { + public static void main(String args[]) { + System.out.println("Nothing to see yet"); + + BulletinBoardMessage msg; + + UnsignedBulletinBoardMessage msgContents = UnsignedBulletinBoardMessage.newBuilder() + .addTag("test") + .setData(ByteString.copyFromUtf8("some data")) + .build(); + + msg = BulletinBoardMessage.newBuilder() + .setMsg(msgContents) + .build(); + + try { + msg.writeTo(System.err); + } catch (IOException e) { + // Ignore + } + } +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java index d74c1ea..ab71a76 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/AsyncBulletinBoardClient.java @@ -1,8 +1,9 @@ package meerkat.bulletinboard; import com.google.common.util.concurrent.FutureCallback; -import com.google.protobuf.ByteString; +import com.google.protobuf.Timestamp; import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Crypto.Signature; import java.util.List; @@ -13,6 +14,7 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient { /** * Post a message to the bulletin board in an asynchronous manner + * The message may be broken up by the client into a batch message, depending on implementation * @param msg is the message to be posted * @param callback is a class containing methods to handle the result of the operation * @return a unique message ID for the message, that can be later used to retrieve the batch @@ -20,56 +22,58 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient { public MessageID postMessage(BulletinBoardMessage msg, FutureCallback callback); /** - * Perform an end-to-end post of a signed batch message - * @param completeBatch contains all the data of the batch including the meta-data and the signature + * Perform an end-to-end post of a message in batch form + * @param completeBatch contains all the data of the batch + * @param chunkSize is the maximum size of each chunk of the message in bytes * @param callback is a class containing methods to handle the result of the operation * @return a unique identifier for the batch message */ - public MessageID postBatch(CompleteBatch completeBatch, FutureCallback callback); + public MessageID postAsBatch(BulletinBoardMessage completeBatch, int chunkSize, FutureCallback callback); + + /** + * An interface for returning an opaque identifier for a batch message + * This identifier is used to uniquely identify the batch until it is completely posted and signed + * After the batch is fully posted: it is identified by its digest (like any message) + * This can be implementation-specific (and not necessarily interchangeable between different implementations) + */ + public interface BatchIdentifier {} /** * This message informs the server about the existence of a new batch message and supplies it with the tags associated with it - * @param beginBatchMessage contains the data required to begin the batch + * @param tags contains the tags used in the batch * @param callback is a callback function class for handling results of the operation + * it receives a BatchIdentifier for use in subsequent batch post operations */ - public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback callback); + public void beginBatch(Iterable tags, FutureCallback callback); /** * This method posts batch data into an (assumed to be open) batch * It does not close the batch - * @param signerId is the canonical form for the ID of the sender of this batch - * @param batchId is a unique (per signer) ID for this batch - * @param batchDataList is the (canonically ordered) list of data comprising the portion of the batch to be posted + * @param batchIdentifier is the temporary batch identifier + * @param batchChunkList is the (canonically ordered) list of data comprising the portion of the batch to be posted * @param startPosition is the location (in the batch) of the first entry in batchDataList - * (optionally used to continue interrupted post operations) - * The first position in the batch is position 0 + * (optionally used to continue interrupted post operations) + * The first position in the batch is position 0 * @param callback is a callback function class for handling results of the operation + * @throws IllegalArgumentException if the batch identifier given was of an illegal format */ - public void postBatchData(byte[] signerId, int batchId, List batchDataList, - int startPosition, FutureCallback callback); + public void postBatchData(BatchIdentifier batchIdentifier, List batchChunkList, + int startPosition, FutureCallback callback) throws IllegalArgumentException; /** * Overloading of the postBatchData method which starts at the first position in the batch */ - public void postBatchData(byte[] signerId, int batchId, List batchDataList, FutureCallback callback); - - /** - * Overloading of the postBatchData method which uses ByteString - */ - public void postBatchData(ByteString signerId, int batchId, List batchDataList, - int startPosition, FutureCallback callback); - - /** - * Overloading of the postBatchData method which uses ByteString and starts at the first position in the batch - */ - public void postBatchData(ByteString signerId, int batchId, List batchDataList, FutureCallback callback); + public void postBatchData(BatchIdentifier batchIdentifier, List batchChunkList, FutureCallback callback) + throws IllegalArgumentException; /** * Attempts to close a batch message - * @param closeBatchMessage contains the data required to close the batch + * @param batchIdentifier is the temporary batch identifier * @param callback is a callback function class for handling results of the operation + * @throws IllegalArgumentException if the batch identifier given was of an illegal format */ - public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback callback); + public void closeBatch(BatchIdentifier batchIdentifier, Timestamp timestamp, Iterable signatures, FutureCallback callback) + throws IllegalArgumentException; /** * Check how "safe" a given message is in an asynchronous manner @@ -83,18 +87,29 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient { * Read all messages posted matching the given filter in an asynchronous manner * Note that if messages haven't been "fully posted", this might return a different * set of messages in different calls. However, messages that are fully posted - * are guaranteed to be included. - * @param filterList return only messages that match the filters (null means no filtering). + * are guaranteed to be included + * Also: batch messages are returned as stubs. + * @param filterList return only messages that match the filters (null means no filtering) * @param callback is a callback function class for handling results of the operation */ public void readMessages(MessageFilterList filterList, FutureCallback> callback); /** - * Read a given batch message from the bulletin board - * @param batchSpecificationMessage contains the data required to specify a single batch instance + * Read a given message from the bulletin board + * If the message is a batch: returns a complete message containing the batch data as well as the metadata + * @param msgID is the ID of the message to be read * @param callback is a callback class for handling the result of the operation */ - public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback callback); + public void readMessage(MessageID msgID, FutureCallback callback); + + /** + * Read batch data for a specific stub message + * @param stub is a batch message stub + * @param callback is a callback class for handling the result of the operation + * @return a new BulletinBoardMessage containing both metadata from the stub and actual data from the server + * @throws IllegalArgumentException if the received message is not a stub + */ + public void readBatchData(BulletinBoardMessage stub, FutureCallback callback) throws IllegalArgumentException; /** diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BatchDigest.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BatchDigest.java deleted file mode 100644 index 6e30fe9..0000000 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BatchDigest.java +++ /dev/null @@ -1,20 +0,0 @@ -package meerkat.bulletinboard; - -import meerkat.crypto.Digest; -import meerkat.protobuf.BulletinBoardAPI.*; - -import java.util.List; - -/** - * Created by Arbel Deutsch Peled on 18-Dec-15. - * Extends the Digest interface with a method for digesting Batch messages - */ -public interface BatchDigest extends Digest { - - /** - * Update the digest with the batch message data (ignore the signature) - * @param completeBatch is the batch message that needs to be digested - */ - public void update(CompleteBatch completeBatch); - -} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BatchDigitalSignature.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BatchDigitalSignature.java deleted file mode 100644 index e04f3c5..0000000 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BatchDigitalSignature.java +++ /dev/null @@ -1,34 +0,0 @@ -package meerkat.bulletinboard; - -import meerkat.crypto.DigitalSignature; -import meerkat.protobuf.BulletinBoardAPI.BeginBatchMessage; -import meerkat.protobuf.BulletinBoardAPI.BatchData; -import meerkat.protobuf.Crypto.Signature; - -import java.security.InvalidKeyException; -import java.security.SignatureException; -import java.security.cert.CertificateException; -import java.util.List; - -/** - * Created by Arbel Deutsch Peled on 20-Dec-15. - * Extends the DigitalSignature interface with methods for signing and authenticating Batch messages - */ -public interface BatchDigitalSignature extends DigitalSignature { - - /** - * Appends the batch data to the signed content (ignoring the signature) - * @param completeBatch contains all the data about the batch - * @throws SignatureException - */ - public void updateContent(CompleteBatch completeBatch) throws SignatureException; - - /** - * Performs a complete verification process on the given batch message - * @param completeBatch contains the batch data as well as the signature - * @return TRUE if the batch is verified and FALSE otherwise - * @throws SignatureException | SignatureException | InvalidKeyException when underlying methods do so - */ - public boolean verify(CompleteBatch completeBatch) throws SignatureException, CertificateException, InvalidKeyException; - -} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java index 1d4343b..c03051c 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java @@ -1,63 +1,92 @@ -package meerkat.bulletinboard; - -import meerkat.comm.CommunicationException; -import meerkat.protobuf.Voting.*; - -import static meerkat.protobuf.BulletinBoardAPI.*; - -import java.util.Collection; -import java.util.List; - -/** - * Created by talm on 24/10/15. - */ -public interface BulletinBoardClient { - - /** - * Initialize the client to use some specified servers - * @param clientParams contains the parameters required for the client setup - */ - void init(BulletinBoardClientParams clientParams); - - /** - * Post a message to the bulletin board in a synchronous manner - * @param msg is the message to be posted - * @return a unique message ID for the message, that can be later used to retrieve the batch - * @throws CommunicationException - */ - MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException; - - /** - * Check how "safe" a given message is in a synchronous manner - * @param id is the unique message identifier for retrieval - * @return a normalized "redundancy score" from 0 (local only) to 1 (fully published) - */ - float getRedundancy(MessageID id); - - /** - * Read all messages posted matching the given filter in a synchronous manner - * Note that if messages haven't been "fully posted", this might return a different - * set of messages in different calls. However, messages that are fully posted - * are guaranteed to be included. - * @param filterList return only messages that match the filters (null means no filtering) - * @return the list of messages - */ - List readMessages(MessageFilterList filterList); - - /** - * Create a SyncQuery to test against that corresponds with the current server state for a specific filter list - * Should only be called on instances for which the actual server contacted is known (i.e. there is only one server) - * @param GenerateSyncQueryParams defines the required information needed to generate the query - * These are represented as fractions of the total number of relevant messages - * @return The generated SyncQuery - * @throws CommunicationException when no DB can be contacted - */ - SyncQuery generateSyncQuery(GenerateSyncQueryParams GenerateSyncQueryParams) throws CommunicationException; - - /** - * Closes all connections, if any. - * This is done in a synchronous (blocking) way. - */ - void close(); - -} +package meerkat.bulletinboard; + +import meerkat.comm.CommunicationException; +import meerkat.protobuf.Voting.*; + +import static meerkat.protobuf.BulletinBoardAPI.*; + +import java.util.List; + +/** + * Created by talm on 24/10/15. + */ +public interface BulletinBoardClient { + + /** + * Initialize the client to use some specified servers + * @param clientParams contains the parameters required for the client setup + */ + void init(BulletinBoardClientParams clientParams); + + /** + * Post a message to the bulletin board in a synchronous manner + * The message may be broken up by the client into a batch message depending on implementation + * @param msg is the message to be posted + * @return a unique message ID for the message, that can be later used to retrieve the batch + * @throws CommunicationException + */ + MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException; + + /** + * Check how "safe" a given message is in a synchronous manner + * @param id is the unique message identifier for retrieval + * @return a normalized "redundancy score" from 0 (local only) to 1 (fully published) + * @throws CommunicationException + */ + float getRedundancy(MessageID id) throws CommunicationException; + + /** + * Read all messages posted matching the given filter in a synchronous manner + * Note that if messages haven't been "fully posted", this might return a different + * set of messages in different calls. However, messages that are fully posted + * are guaranteed to be included. + * Also: batch messages are returned as stubs. + * @param filterList return only messages that match the filters (null means no filtering) + * @return the list of messages + */ + List readMessages(MessageFilterList filterList) throws CommunicationException; + + /** + * Breaks up a bulletin board message into chunks and posts it as a batch message + * @param msg is the message to post + * @param chunkSize is the maximal chunk size in bytes + * @return the unique message ID + * @throws CommunicationException if operation is unsuccessful + */ + MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException; + + /** + * Read a given message from the bulletin board + * If the message is a batch: returns a complete message containing the batch data as well as the metadata + * @param msgID is the ID of the message to be read + * @return the complete message + * @throws CommunicationException if operation is unsuccessful + */ + BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException; + + /** + * Read batch data for a specific stub message + * @param stub is a batch message stub + * @return a new BulletinBoardMessage containing both metadata from the stub and actual data from the server + * @throws CommunicationException if operation is unsuccessful + * @throws IllegalArgumentException if the received message is not a stub + */ + BulletinBoardMessage readBatchData(BulletinBoardMessage stub) throws CommunicationException, IllegalArgumentException; + + /** + * Create a SyncQuery to test against that corresponds with the current server state for a specific filter list + * Should only be called on instances for which the actual server contacted is known (i.e. there is only one server) + * @param generateSyncQueryParams defines the required information needed to generate the query + * These are represented as fractions of the total number of relevant messages + * @return The generated SyncQuery + * @throws CommunicationException when no DB can be contacted + */ + SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException; + + /** + * Closes all connections, if any. + * This is done in a synchronous (blocking) way. + */ + void close(); + +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardConstants.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardConstants.java index 9ddee90..9404165 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardConstants.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardConstants.java @@ -10,6 +10,7 @@ public interface BulletinBoardConstants { public static final String BULLETIN_BOARD_SERVER_PATH = "/bbserver"; public static final String GENERATE_SYNC_QUERY_PATH = "/generatesyncquery"; public static final String READ_MESSAGES_PATH = "/readmessages"; + public static final String COUNT_MESSAGES_PATH = "/countmessages"; public static final String READ_BATCH_PATH = "/readbatch"; public static final String POST_MESSAGE_PATH = "/postmessage"; public static final String BEGIN_BATCH_PATH = "/beginbatch"; @@ -17,9 +18,4 @@ public interface BulletinBoardConstants { public static final String CLOSE_BATCH_PATH = "/closebatch"; public static final String SYNC_QUERY_PATH = "/syncquery"; - // Other Constants - - public static final String BATCH_TAG = "@BATCH"; - public static final String BATCH_ID_TAG_PREFIX = "BATCHID#"; - } diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardDigest.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardDigest.java new file mode 100644 index 0000000..3e47c23 --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardDigest.java @@ -0,0 +1,30 @@ +package meerkat.bulletinboard; + +import meerkat.crypto.Digest; +import meerkat.protobuf.BulletinBoardAPI.*; + +import java.util.List; + +/** + * Created by Arbel Deutsch Peled on 18-Dec-15. + * Extends the Digest interface with methods for digesting Bulletin Board messages + */ +public interface BulletinBoardDigest extends Digest { + + /** + * Update the digest with the message data (ignore the signature) + * The digest only uses the part the signatures are computed on for this operation + * If the message is a stub: this should be called before digesting the raw data + * @param msg is the message that needs to be digested + */ + public void update(BulletinBoardMessage msg); + + /** + * Update the digest with the message data (ignore the signature) + * The digest only uses the part the signatures are computed on for this operation + * If the message is a stub: this should be called before digesting the raw data + * @param msg is the message that needs to be digested + */ + public void update(UnsignedBulletinBoardMessage msg); + +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardMessageDeleter.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardMessageDeleter.java new file mode 100644 index 0000000..cf57975 --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardMessageDeleter.java @@ -0,0 +1,49 @@ +package meerkat.bulletinboard; + +import com.google.common.util.concurrent.FutureCallback; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.BulletinBoardAPI.*; + +/** + * Created by Arbel Deutsch Peled on 13-Apr-16. + * This interface is meant to extend a BulletinBoardClient interface/class + * It provides it with the ability to delete messages from the Server + * This assumes the Server implements the {@link DeletableBulletinBoardServer} + */ +public interface BulletinBoardMessageDeleter { + + /** + * Deletes a message from a Bulletin Board Server in a possibly asynchronous manner + * Logs this action + * @param msgID is the ID of the message to delete + * @param callback handles the result of the operation + */ + public void deleteMessage(MessageID msgID, FutureCallback callback); + + /** + * Deletes a message from the Bulletin Board in a possibly asynchronous manner + * Logs this action + * @param entryNum is the serial entry number of the message to delete + * @param callback handles the result of the operation + */ + public void deleteMessage(long entryNum, FutureCallback callback); + + /** + * Deletes a message from a Bulletin Board Server in a synchronous manner + * Logs this action + * @param msgID is the ID of the message to delete + * @return TRUE if the message was deleted and FALSE if it did not exist on the server + * @throws CommunicationException when an error occurs + */ + public boolean deleteMessage(MessageID msgID) throws CommunicationException; + + /** + * Deletes a message from the Bulletin Board in a synchronous manner + * Logs this action + * @param entryNum is the serial entry number of the message to delete + * @return TRUE if the message was deleted and FALSE if it did not exist on the server + * @throws CommunicationException when an error occurs + */ + public boolean deleteMessage(long entryNum) throws CommunicationException; + +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java index 1507ab7..40f8ab3 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java @@ -1,104 +1,110 @@ -package meerkat.bulletinboard; - -import com.google.protobuf.BoolValue; -import meerkat.comm.CommunicationException; -import meerkat.comm.MessageOutputStream; -import meerkat.protobuf.BulletinBoardAPI.*; - -import java.util.Collection; - - -/** - * Created by Arbel on 07/11/15. - * - * This interface refers to a single instance of a Bulletin Board - * An implementation of this interface may use any DB and be hosted on any machine. - */ - -public interface BulletinBoardServer{ - - /** - * This method initializes the server by reading the signature data and storing it - * It also establishes the connection to the DB - * @throws CommunicationException on DB connection error - */ - public void init(String meerkatDB) throws CommunicationException; - - /** - * Post a message to bulletin board. - * @param msg is the actual (signed) message - * @return TRUE if the message has been authenticated and FALSE otherwise (in ProtoBuf form) - * @throws CommunicationException on DB connection error - */ - public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException; - - /** - * Read all messages posted matching the given filter - * @param filterList return only messages that match the filters (empty list or null means no filtering) - * @param out is an output stream into which the matching messages are written - * @throws CommunicationException on DB connection error - */ - public void readMessages(MessageFilterList filterList, MessageOutputStream out) throws CommunicationException; - - /** - * Informs server about a new batch message - * @param message contains the required data about the new batch - * @return TRUE if the batch request is accepted amd FALSE otherwise - * Specifically, if such a batch already exists and is not yet closed: the value returned will be TRUE - * However, if such a batch exists and is already closed: the value returned will be FALSE - * @throws CommunicationException on DB connection error - */ - public BoolValue beginBatch(BeginBatchMessage message) throws CommunicationException; - - /** - * Posts a (part of a) batch message to the bulletin board - * Note that the existence and contents of a batch message are not available for reading before the batch is finalized - * @param batchMessage contains the (partial) data this message carries as well as meta-data required in order to place the data - * in the correct position inside the correct batch - * @return TRUE if the message is accepted and successfully saved and FALSE otherwise - * Specifically, if the batch is already closed: the value returned will be FALSE - * However, requiring to open a batch before insertion of messages is implementation-dependent - * @throws CommunicationException on DB connection error - */ - public BoolValue postBatchMessage(BatchMessage batchMessage) throws CommunicationException; - - /** - * Attempts to stop and finalize a batch message - * @param message contains the data necessary to stop the batch; in particular: the signature for the batch - * @return TRUE if the batch was successfully closed, FALSE otherwise - * Specifically, if the signature is invalid or if some of the batch parts have not yet been submitted: the value returned will be FALSE - * @throws CommunicationException on DB connection error - */ - public BoolValue closeBatchMessage(CloseBatchMessage message) throws CommunicationException; - - /** - * Reads a batch message from the server (starting with the supplied position) - * @param message specifies the signer ID and the batch ID to read as well as an (optional) start position - * @param out is a stream of the ordered batch messages starting from the specified start position (if given) or from the beginning (if omitted) - * @throws CommunicationException on DB connection error - * @throws IllegalArgumentException if message does not specify a batch - */ - public void readBatch(BatchSpecificationMessage message, MessageOutputStream out) throws CommunicationException, IllegalArgumentException; - - /** - * Create a SyncQuery to test against that corresponds with the current server state for a specific filter list - * @param generateSyncQueryParams defines the information needed to generate the query - * @return The generated SyncQuery - * @throws CommunicationException on DB connection error - */ - SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException; - - /** - * Queries the database for sync status with respect to a given sync query - * @param syncQuery contains a succinct representation of states to compare to - * @return a SyncQueryResponse object containing the representation of the most recent state the database matches - * @throws CommunicationException - */ - public SyncQueryResponse querySync(SyncQuery syncQuery) throws CommunicationException; - - /** - * This method closes the connection to the DB - * @throws CommunicationException on DB connection error - */ - public void close() throws CommunicationException; -} +package meerkat.bulletinboard; + +import com.google.protobuf.BoolValue; +import com.google.protobuf.Int32Value; +import com.google.protobuf.Int64Value; +import meerkat.comm.CommunicationException; +import meerkat.comm.MessageOutputStream; +import meerkat.protobuf.BulletinBoardAPI.*; + + +/** + * Created by Arbel on 07/11/15. + * + * This interface refers to a single instance of a Bulletin Board + * An implementation of this interface may use any DB and be hosted on any machine. + */ + +public interface BulletinBoardServer{ + + /** + * This method initializes the server by reading the signature data and storing it + * It also establishes the connection to the DB + * @throws CommunicationException on DB connection error + */ + public void init() throws CommunicationException; + + /** + * Post a message to bulletin board. + * @param msg is the actual (signed) message + * @return TRUE if the message has been authenticated and FALSE otherwise (in ProtoBuf form) + * @throws CommunicationException on DB connection error + */ + public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException; + + /** + * Read all posted messages matching the given filters + * @param filterList return only messages that match the filters (empty list or null means no filtering) + * @param out is an output stream into which the matching messages are written + * @throws CommunicationException on DB connection error + */ + public void readMessages(MessageFilterList filterList, MessageOutputStream out) throws CommunicationException; + + /** + * Return the number of posted messages matching the given filters + * @param filterList count only messages that match the filters (empty list or null means no filtering) + * @return an IntMsg containing the number of messages that match the filter + * @throws CommunicationException on DB connection error + */ + public Int32Value getMessageCount(MessageFilterList filterList) throws CommunicationException; + + /** + * Informs server about a new batch message + * @param message contains the required data about the new batch + * @return a unique batch identifier for the new batch ; -1 if batch creation was unsuccessful + * @throws CommunicationException on DB connection error + */ + public Int64Value beginBatch(BeginBatchMessage message) throws CommunicationException; + + /** + * Posts a chunk of a batch message to the bulletin board + * Note that the existence and contents of a batch message are not available for reading before the batch is finalized + * @param batchMessage contains the (partial) data this message carries as well as meta-data required in order to place the data + * in the correct position inside the correct batch + * @return TRUE if the message is accepted and successfully saved and FALSE otherwise + * Specifically, if the batch is already closed: the value returned will be FALSE + * However, requiring to open a batch before insertion of messages is implementation-dependent + * @throws CommunicationException on DB connection error + */ + public BoolValue postBatchMessage(BatchMessage batchMessage) throws CommunicationException; + + /** + * Attempts to close and finalize a batch message + * @param message contains the data necessary to close the batch; in particular: the signature for the batch + * @return TRUE if the batch was successfully closed, FALSE otherwise + * Specifically, if the signature is invalid or if some of the batch parts have not yet been submitted: the value returned will be FALSE + * @throws CommunicationException on DB connection error + */ + public BoolValue closeBatch(CloseBatchMessage message) throws CommunicationException; + + /** + * Reads a batch message from the server (starting with the supplied position) + * @param batchQuery specifies which batch and what parts of it to retrieve + * @param out is a stream of the ordered batch messages starting from the specified start position (if given) or from the beginning (if omitted) + * @throws CommunicationException on DB connection error + * @throws IllegalArgumentException if message ID does not specify a batch + */ + public void readBatch(BatchQuery batchQuery, MessageOutputStream out) throws CommunicationException, IllegalArgumentException; + + /** + * Create a SyncQuery to test against that corresponds with the current server state for a specific filter list + * @param generateSyncQueryParams defines the information needed to generate the query + * @return The generated SyncQuery + * @throws CommunicationException on DB connection error + */ + public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException; + + /** + * Queries the database for sync status with respect to a given sync query + * @param syncQuery contains a succinct representation of states to compare to + * @return a SyncQueryResponse object containing the representation of the most recent state the database matches + * @throws CommunicationException + */ + public SyncQueryResponse querySync(SyncQuery syncQuery) throws CommunicationException; + + /** + * This method closes the connection to the DB + * @throws CommunicationException on DB connection error + */ + public void close() throws CommunicationException; +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSignature.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSignature.java new file mode 100644 index 0000000..ba018ca --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSignature.java @@ -0,0 +1,31 @@ +package meerkat.bulletinboard; + +import meerkat.crypto.DigitalSignature; +import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage; +import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage; +import meerkat.protobuf.Crypto; + +import java.security.SignatureException; + +/** + * Created by Arbel Deutsch Peled on 18-Dec-15. + * Extends the DigitalSignature interface with methods for signing Bulletin Board messages + */ +public interface BulletinBoardSignature extends DigitalSignature { + + /** + * Add msg to the content stream to be verified / signed + * The digest only uses the part the signatures are computed on for this operation + * If the message is a stub: this should be called before updating with the raw data + * @param msg is the message that needs to be digested + */ + public void updateContent(BulletinBoardMessage msg) throws SignatureException; + + /** + * Add msg to the content stream to be verified / signed + * If the message is a stub: this should be called before updating with the raw data + * @param msg is the message that needs to be digested + */ + public void updateContent(UnsignedBulletinBoardMessage msg) throws SignatureException; + +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSynchronizer.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSynchronizer.java index 4b07225..c25d737 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSynchronizer.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardSynchronizer.java @@ -1,22 +1,84 @@ package meerkat.bulletinboard; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.BulletinBoardAPI.*; + +import com.google.common.util.concurrent.FutureCallback; + +import java.util.List; + /** * Created by Arbel Deutsch Peled on 08-Mar-16. * This interface defines the behaviour of a bulletin board synchronizer * This is used to make sure that data in a specific instance of a bulletin board server is duplicated to a sufficient percentage of the other servers */ -public interface BulletinBoardSynchronizer extends Runnable{ +public interface BulletinBoardSynchronizer extends Runnable { + + public enum SyncStatus{ + SYNCHRONIZED, // No more messages to upload + PENDING, // Synchronizer is 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 + } /** - * - * @param localClient is a client for the local DB instance - * @param remoteClient is a client for the remote DBs - * @param minRedundancy + * Initializes the synchronizer with the required data to function properly + * @param localClient is a client for the temporary local storage server which contains only data to be uploaded + * @param remoteClient is a client for the remote servers into which the data needs to be uploaded */ - public void init(BulletinBoardClient localClient, AsyncBulletinBoardClient remoteClient, float minRedundancy); + public void init(DeletableSubscriptionBulletinBoardClient localClient, AsyncBulletinBoardClient remoteClient); + /** + * Returns the current server synchronization status + * @return the current synchronization status + */ + public SyncStatus getSyncStatus(); + + /** + * Creates a subscription to sync status changes + * @param callback is the handler for any status changes + */ + public void subscribeToSyncStatus(FutureCallback callback); + + /** + * Returns the messages which have not yet been synchronized + * @return the list of messages remaining to be synchronized + */ + public List getRemainingMessages() throws CommunicationException; + + /** + * Asynchronously returns the messages which have not yet been synchronized + * @param callback is the handler for the list of messages + */ + public void getRemainingMessages(FutureCallback> callback); + + /** + * Returns the current number of unsynchronized messages + * @return the current synchronization status + */ + public long getRemainingMessagesCount() throws CommunicationException; + + /** + * Creates a subscription to changes in the number of unsynchronized messages + * @param callback is the handler for any status changes + */ + public void subscribeToRemainingMessagesCount(FutureCallback callback); + + /** + * Starts the synchronization + */ @Override public void run(); + /** + * Lets the Synchronizer know that there is new data to be uploaded + * This is used to reduce the latency between local data-writes and uploads to the remote servers + */ + public void nudge(); + + /** + * Stops the synchronization + */ + public void stop(); } diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/CompleteBatch.java b/meerkat-common/src/main/java/meerkat/bulletinboard/CompleteBatch.java deleted file mode 100644 index 649fd8b..0000000 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/CompleteBatch.java +++ /dev/null @@ -1,150 +0,0 @@ -package meerkat.bulletinboard; - -import com.google.protobuf.Timestamp; -import meerkat.protobuf.BulletinBoardAPI.*; -import meerkat.protobuf.Crypto.*; -import meerkat.util.BulletinBoardMessageComparator; - -import java.util.LinkedList; -import java.util.List; - -/** - * Created by Arbel Deutsch Peled on 14-Dec-15. - * - * A data structure for holding a complete batch message along with its signature - */ -public class CompleteBatch { - - private BeginBatchMessage beginBatchMessage; - private List batchDataList; - private Signature signature; - private Timestamp timestamp; - - public CompleteBatch() { - batchDataList = new LinkedList(); - } - - public CompleteBatch(BeginBatchMessage newBeginBatchMessage) { - this(); - beginBatchMessage = newBeginBatchMessage; - } - - public CompleteBatch(BeginBatchMessage newBeginBatchMessage, List newDataList) { - this(newBeginBatchMessage); - appendBatchData(newDataList); - } - - public CompleteBatch(BeginBatchMessage newBeginBatchMessage, List newDataList, Signature newSignature) { - this(newBeginBatchMessage, newDataList); - signature = newSignature; - } - - public CompleteBatch(BeginBatchMessage newBeginBatchMessage, List newDataList, Signature newSignature, Timestamp timestamp) { - this(newBeginBatchMessage, newDataList, newSignature); - this.timestamp = timestamp; - } - - public CompleteBatch(Timestamp timestamp) { - this(); - this.timestamp = timestamp; - } - - public BeginBatchMessage getBeginBatchMessage() { - return beginBatchMessage; - } - - public List getBatchDataList() { - return batchDataList; - } - - public Signature getSignature() { - return signature; - } - - public Timestamp getTimestamp() { - return timestamp; - } - - public CloseBatchMessage getCloseBatchMessage() { - return CloseBatchMessage.newBuilder() - .setBatchId(getBeginBatchMessage().getBatchId()) - .setBatchLength(getBatchDataList().size()) - .setSig(getSignature()) - .setTimestamp(getTimestamp()) - .build(); - } - - public void setBeginBatchMessage(BeginBatchMessage beginBatchMessage) { - this.beginBatchMessage = beginBatchMessage; - } - - public void appendBatchData(BatchData newBatchData) { - batchDataList.add(newBatchData); - } - - public void appendBatchData(List newBatchDataList) { - batchDataList.addAll(newBatchDataList); - } - - public void setSignature(Signature newSignature) { - signature = newSignature; - } - - public void setTimestamp(Timestamp timestamp) { - this.timestamp = timestamp; - } - - @Override - public boolean equals(Object other) { - - if (!(other instanceof CompleteBatch)) { - return false; - } - - CompleteBatch otherBatch = (CompleteBatch) other; - - boolean result = true; - - if (beginBatchMessage == null) { - if (otherBatch.getBeginBatchMessage() != null) - return false; - } else { - result = result && beginBatchMessage.equals(otherBatch.getBeginBatchMessage()); - } - - if (batchDataList == null) { - if (otherBatch.getBatchDataList() != null) - return false; - } else { - result = result && batchDataList.equals(otherBatch.getBatchDataList()); - } - - if (signature == null) { - if (otherBatch.getSignature() != null) - return false; - } else { - result = result && signature.equals(otherBatch.getSignature()); - } - - if (timestamp == null) { - if (otherBatch.getTimestamp() != null) - return false; - } else { - result = result && timestamp.equals(otherBatch.getTimestamp()); - } - - return result; - - } - - @Override - public String toString() { - - if (beginBatchMessage == null || beginBatchMessage.getSignerId() == null) - return "Unspecified batch " + super.toString(); - - return "Batch " + beginBatchMessage.getSignerId().toString() + ":" + beginBatchMessage.getBatchId(); - - } - -} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/DeletableBulletinBoardServer.java b/meerkat-common/src/main/java/meerkat/bulletinboard/DeletableBulletinBoardServer.java new file mode 100644 index 0000000..cfff084 --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/DeletableBulletinBoardServer.java @@ -0,0 +1,32 @@ +package meerkat.bulletinboard; + +import com.google.protobuf.BoolValue; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.BulletinBoardAPI.*; + +/** + * Created by Arbel Deutsch Peled on 13-Apr-16. + */ +public interface DeletableBulletinBoardServer extends BulletinBoardServer { + + /** + * Deletes a message from the Bulletin Board + * If the message is a batch: the batch data is deleted as well + * Logs this action + * @param msgID is the ID of the message to delete + * @return a BoolMsg containing the value TRUE if a message was deleted, FALSE if the message does not exist + * @throws CommunicationException in case of an error + */ + public BoolValue deleteMessage(MessageID msgID) throws CommunicationException; + + /** + * Deletes a message from the Bulletin Board + * If the message is a batch: the batch data is deleted as well + * Logs this action + * @param entryNum is the serial entry number of the message to delete + * @return a BoolMsg containing the value TRUE if a message was deleted, FALSE if the message does not exist + * @throws CommunicationException in case of an error + */ + public BoolValue deleteMessage(long entryNum) throws CommunicationException; + +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/DeletableSubscriptionBulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/DeletableSubscriptionBulletinBoardClient.java new file mode 100644 index 0000000..acc29fe --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/DeletableSubscriptionBulletinBoardClient.java @@ -0,0 +1,7 @@ +package meerkat.bulletinboard; + +/** + * Created by Arbel Deutsch Peled on 13-Apr-16. + */ +public interface DeletableSubscriptionBulletinBoardClient extends SubscriptionBulletinBoardClient, BulletinBoardMessageDeleter { +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBatchDigest.java b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBatchDigest.java deleted file mode 100644 index 852bb24..0000000 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBatchDigest.java +++ /dev/null @@ -1,61 +0,0 @@ -package meerkat.bulletinboard; - -import com.google.protobuf.Message; -import meerkat.crypto.Digest; -import meerkat.protobuf.BulletinBoardAPI.MessageID; -import meerkat.protobuf.BulletinBoardAPI.BatchData; - -import java.util.List; - - -/** - * Created by Arbel Deutsch Peled on 19-Dec-15. - * Wrapper class for digesting Batches in a standardized way - */ -public class GenericBatchDigest implements BatchDigest{ - - private Digest digest; - - public GenericBatchDigest(Digest digest) { - this.digest = digest; - } - - @Override - public void update(CompleteBatch completeBatch) { - - update(completeBatch.getBeginBatchMessage()); - - for (BatchData batchData : completeBatch.getBatchDataList()) { - update(batchData); - } - - update(completeBatch.getTimestamp()); - - } - - @Override - public byte[] digest() { - return digest.digest(); - } - - @Override - public MessageID digestAsMessageID() { - return digest.digestAsMessageID(); - } - - @Override - public void update(Message msg) { - digest.update(msg); - } - - @Override - public void reset() { - digest.reset(); - } - - @Override - public GenericBatchDigest clone() throws CloneNotSupportedException{ - return new GenericBatchDigest(digest.clone()); - } - -} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBatchDigitalSignature.java b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBatchDigitalSignature.java deleted file mode 100644 index 7327b8e..0000000 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBatchDigitalSignature.java +++ /dev/null @@ -1,104 +0,0 @@ -package meerkat.bulletinboard; - -import com.google.protobuf.ByteString; -import com.google.protobuf.Message; -import meerkat.crypto.DigitalSignature; -import meerkat.protobuf.BulletinBoardAPI.BatchData; -import meerkat.protobuf.Crypto; - -import java.io.IOException; -import java.io.InputStream; -import java.security.InvalidKeyException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.SignatureException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; - -/** - * Created by Arbel Deutsch Peled on 20-Dec-15. - * Wrapper class for signing and verifying Batch signatures in a standardized way - */ -public class GenericBatchDigitalSignature implements BatchDigitalSignature{ - - private DigitalSignature digitalSignature; - - public GenericBatchDigitalSignature(DigitalSignature digitalSignature) { - this.digitalSignature = digitalSignature; - } - - @Override - public void updateContent(CompleteBatch completeBatch) throws SignatureException { - - digitalSignature.updateContent(completeBatch.getBeginBatchMessage()); - - for (BatchData batchData : completeBatch.getBatchDataList()) { - digitalSignature.updateContent(batchData); - } - - digitalSignature.updateContent(completeBatch.getTimestamp()); - - } - - @Override - public boolean verify(CompleteBatch completeBatch) throws SignatureException, CertificateException, InvalidKeyException { - - digitalSignature.initVerify(completeBatch.getSignature()); - updateContent(completeBatch); - return digitalSignature.verify(); - - } - - @Override - public void loadVerificationCertificates(InputStream certStream) throws CertificateException { - digitalSignature.loadVerificationCertificates(certStream); - } - - @Override - public void clearVerificationCertificates() { - digitalSignature.clearVerificationCertificates(); - } - - @Override - public void updateContent(Message msg) throws SignatureException { - digitalSignature.updateContent(msg); - } - - @Override - public Crypto.Signature sign() throws SignatureException { - return digitalSignature.sign(); - } - - @Override - public void initVerify(Crypto.Signature sig) throws CertificateException, InvalidKeyException { - digitalSignature.initVerify(sig); - } - - @Override - public boolean verify() { - return digitalSignature.verify(); - } - - @Override - public KeyStore.Builder getPKCS12KeyStoreBuilder(InputStream keyStream, char[] password) - throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException { - return digitalSignature.getPKCS12KeyStoreBuilder(keyStream, password); - } - - @Override - public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder) throws IOException, CertificateException, UnrecoverableKeyException { - digitalSignature.loadSigningCertificate(keyStoreBuilder); - } - - @Override - public ByteString getSignerID() { - return digitalSignature.getSignerID(); - } - - @Override - public void clearSigningKey() { - digitalSignature.clearSigningKey(); - } - -} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardDigest.java b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardDigest.java new file mode 100644 index 0000000..fb7bd3f --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardDigest.java @@ -0,0 +1,74 @@ +package meerkat.bulletinboard; + +import com.google.protobuf.ByteString; +import com.google.protobuf.BytesValue; +import com.google.protobuf.Message; +import meerkat.crypto.Digest; +import meerkat.protobuf.BulletinBoardAPI; +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.BulletinBoardAPI.MessageID; + + +/** + * Created by Arbel Deutsch Peled on 19-Dec-15. + * Wrapper class for digesting Batches in a standardized way + */ +public class GenericBulletinBoardDigest implements BulletinBoardDigest { + + private Digest digest; + + public GenericBulletinBoardDigest(Digest digest) { + this.digest = digest; + } + + @Override + public byte[] digest() { + return digest.digest(); + } + + @Override + public MessageID digestAsMessageID() { + return digest.digestAsMessageID(); + } + + @Override + public void update(Message msg) { + digest.update(msg); + } + + @Override + public void update(byte[] data) { + digest.update(data); + } + + @Override + public void reset() { + digest.reset(); + } + + @Override + public GenericBulletinBoardDigest clone() throws CloneNotSupportedException{ + return new GenericBulletinBoardDigest(digest.clone()); + } + + @Override + public void update(BulletinBoardMessage msg) { + update(msg.getMsg()); + } + + @Override + public void update(UnsignedBulletinBoardMessage msg) { + + for (ByteString tag : msg.getTagList().asByteStringList()){ + update(tag.toByteArray()); + } + + update(msg.getTimestamp()); + + if (msg.getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.DATA){ + update(msg.getData().toByteArray()); + } + + } + +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardSignature.java b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardSignature.java new file mode 100644 index 0000000..aad6466 --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardSignature.java @@ -0,0 +1,106 @@ +package meerkat.bulletinboard; + +import com.google.protobuf.ByteString; +import com.google.protobuf.Message; +import meerkat.crypto.Digest; +import meerkat.crypto.DigitalSignature; +import meerkat.protobuf.BulletinBoardAPI; +import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage; +import meerkat.protobuf.BulletinBoardAPI.MessageID; +import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage; +import meerkat.protobuf.Crypto; + +import java.io.IOException; +import java.io.InputStream; +import java.security.*; +import java.security.cert.CertificateException; + + +/** + * Created by Arbel Deutsch Peled on 19-Dec-15. + * Wrapper class for digesting Batches in a standardized way + */ +public class GenericBulletinBoardSignature implements BulletinBoardSignature { + + private DigitalSignature signer; + + public GenericBulletinBoardSignature(DigitalSignature signer) { + this.signer = signer; + } + + @Override + public void updateContent(BulletinBoardMessage msg) throws SignatureException{ + signer.updateContent(msg.getMsg()); + } + + @Override + public void updateContent(UnsignedBulletinBoardMessage msg) throws SignatureException{ + + for (ByteString tag : msg.getTagList().asByteStringList()){ + updateContent(tag.toByteArray()); + } + + updateContent(msg.getTimestamp()); + + if (msg.getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.DATA){ + updateContent(msg.getData().toByteArray()); + } + + } + + @Override + public void loadVerificationCertificates(InputStream certStream) throws CertificateException { + signer.loadVerificationCertificates(certStream); + } + + @Override + public void clearVerificationCertificates() { + signer.clearVerificationCertificates(); + } + + @Override + public void updateContent(byte[] data) throws SignatureException { + signer.updateContent(data); + } + + @Override + public void updateContent(Message msg) throws SignatureException { + signer.updateContent(msg); + } + + @Override + public Crypto.Signature sign() throws SignatureException { + return signer.sign(); + } + + @Override + public void initVerify(Crypto.Signature sig) throws CertificateException, InvalidKeyException { + signer.initVerify(sig); + } + + @Override + public boolean verify() { + return signer.verify(); + } + + @Override + public KeyStore.Builder getPKCS12KeyStoreBuilder(InputStream keyStream, char[] password) throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException { + return signer.getPKCS12KeyStoreBuilder(keyStream, password); + } + + @Override + public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder) throws IOException, CertificateException, UnrecoverableKeyException { + signer.loadSigningCertificate(keyStoreBuilder); + } + + @Override + public ByteString getSignerID() { + return signer.getSignerID(); + } + + @Override + public void clearSigningKey() { + signer.clearSigningKey(); + } + +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/SubscriptionAsyncBulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/SubscriptionAsyncBulletinBoardClient.java deleted file mode 100644 index b07e655..0000000 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/SubscriptionAsyncBulletinBoardClient.java +++ /dev/null @@ -1,7 +0,0 @@ -package meerkat.bulletinboard; - -/** - * Created by Arbel Deutsch Peled on 03-Mar-16. - */ -public interface SubscriptionAsyncBulletinBoardClient extends AsyncBulletinBoardClient, BulletinBoardSubscriber { -} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/SubscriptionBulletinBoardClient.java b/meerkat-common/src/main/java/meerkat/bulletinboard/SubscriptionBulletinBoardClient.java new file mode 100644 index 0000000..5577bac --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/SubscriptionBulletinBoardClient.java @@ -0,0 +1,7 @@ +package meerkat.bulletinboard; + +/** + * Created by Arbel Deutsch Peled on 03-Mar-16. + */ +public interface SubscriptionBulletinBoardClient extends AsyncBulletinBoardClient, BulletinBoardSubscriber { +} diff --git a/meerkat-common/src/main/java/meerkat/comm/CommunicationException.java b/meerkat-common/src/main/java/meerkat/comm/CommunicationException.java index f96dced..05e7a22 100644 --- a/meerkat-common/src/main/java/meerkat/comm/CommunicationException.java +++ b/meerkat-common/src/main/java/meerkat/comm/CommunicationException.java @@ -1,36 +1,36 @@ -package meerkat.comm; - -/** - * Created by talm on 24/10/15. - */ -public class CommunicationException extends Exception { - - /** - * Generated serial. - */ - - private static final long serialVersionUID = 2279440129497891293L; - private String message; - - /** - * Default constructor. To be used only if error type is unknown. - */ - public CommunicationException(){ - message = "Unknown communication exception"; - } - - /** - * Constructor enabling specifying of an error message. - * @param errorMessage - */ - public CommunicationException(String errorMessage){ - message = errorMessage; - } - - /** - * @return the error message specified. - */ - public String getMessage(){ - return message; - } -} +package meerkat.comm; + +/** + * Created by talm on 24/10/15. + */ +public class CommunicationException extends Exception { + + /** + * Generated serial. + */ + + private static final long serialVersionUID = 2279440129497891293L; + private String message; + + /** + * Default constructor. To be used only if error type is unknown. + */ + public CommunicationException(){ + message = "Unknown communication exception"; + } + + /** + * Constructor enabling specifying of an error message. + * @param errorMessage + */ + public CommunicationException(String errorMessage){ + message = errorMessage; + } + + /** + * @return the error message specified. + */ + public String getMessage(){ + return message; + } +} diff --git a/meerkat-common/src/main/java/meerkat/crypto/Digest.java b/meerkat-common/src/main/java/meerkat/crypto/Digest.java index b7d86dc..e5202d6 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/Digest.java +++ b/meerkat-common/src/main/java/meerkat/crypto/Digest.java @@ -22,6 +22,12 @@ public interface Digest { */ public MessageID digestAsMessageID(); + /** + * Updates the digest using the given raw data + * @param data contains the raw data + */ + public void update (byte[] data); + /** * Updates the digest using the specified message (in serialized wire form) * diff --git a/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java b/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java index 76c32a6..707e500 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java +++ b/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java @@ -39,6 +39,13 @@ public interface DigitalSignature { */ public void clearVerificationCertificates(); + /** + * Add raw data to the content stream to be verified / signed. + * + * @param data + * @throws SignatureException + */ + public void updateContent(byte[] data) throws SignatureException; /** * Add msg to the content stream to be verified / signed. Each message is (automatically) diff --git a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java index ab8084b..c51f2f2 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java +++ b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java @@ -140,6 +140,11 @@ public class ECDSASignature implements DigitalSignature { signer.update(msg.toByteString().asReadOnlyByteBuffer()); } + @Override + public void updateContent(byte[] data) throws SignatureException { + signer.update(data); + } + public void updateContent(InputStream in) throws IOException, SignatureException { ByteString inStr = ByteString.readFrom(in); signer.update(inStr.asReadOnlyByteBuffer()); diff --git a/meerkat-common/src/main/java/meerkat/crypto/concrete/SHA256Digest.java b/meerkat-common/src/main/java/meerkat/crypto/concrete/SHA256Digest.java index a7723ec..88f417c 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/concrete/SHA256Digest.java +++ b/meerkat-common/src/main/java/meerkat/crypto/concrete/SHA256Digest.java @@ -80,6 +80,7 @@ public class SHA256Digest implements Digest { hash.update(msg.asReadOnlyByteBuffer()); } + @Override final public void update(byte[] msg) { hash.update(msg); } diff --git a/meerkat-common/src/main/java/meerkat/logging/LogVerifier.java b/meerkat-common/src/main/java/meerkat/logging/LogVerifier.java index f27f740..7ba928a 100644 --- a/meerkat-common/src/main/java/meerkat/logging/LogVerifier.java +++ b/meerkat-common/src/main/java/meerkat/logging/LogVerifier.java @@ -1,7 +1,7 @@ -package meerkat.logging; - -/** - * Created by talm on 25/10/15. - */ -public class LogVerifier { -} +package meerkat.logging; + +/** + * Created by talm on 25/10/15. + */ +public class LogVerifier { +} diff --git a/meerkat-common/src/main/java/meerkat/logging/Logger.java b/meerkat-common/src/main/java/meerkat/logging/Logger.java index 26ec424..343ff31 100644 --- a/meerkat-common/src/main/java/meerkat/logging/Logger.java +++ b/meerkat-common/src/main/java/meerkat/logging/Logger.java @@ -1,7 +1,7 @@ -package meerkat.logging; - -/** - * Created by talm on 25/10/15. - */ -public class Logger { -} +package meerkat.logging; + +/** + * Created by talm on 25/10/15. + */ +public class Logger { +} diff --git a/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageComparator.java b/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageComparator.java index 653f130..7005568 100644 --- a/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageComparator.java +++ b/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageComparator.java @@ -1,49 +1,83 @@ -package meerkat.util; - -import meerkat.protobuf.BulletinBoardAPI; -import meerkat.protobuf.BulletinBoardAPI.*; -import meerkat.protobuf.Crypto.*; - -import java.util.Comparator; -import java.util.List; - -/** - * Created by Arbel Deutsch Peled on 05-Dec-15. - * This class implements a comparison between BulletinBoardMessage instances that disregards: - * 1. The entry number (since this can be different between database instances) - * 2. The order of the signatures - */ -public class BulletinBoardMessageComparator implements Comparator { - - /** - * Compare the messages - * @param msg1 - * @param msg2 - * @return 0 if the messages are equivalent (see above) and -1 otherwise. - */ - @Override - public int compare(BulletinBoardMessage msg1, BulletinBoardMessage msg2) { - - List msg1Sigs = msg1.getSigList(); - List msg2Sigs = msg2.getSigList(); - - // Compare unsigned message - if (!msg1.getMsg().equals(msg2.getMsg())){ - return -1; - } - - // Compare signatures - - if (msg1Sigs.size() != msg2Sigs.size()){ - return -1; - } - - for (Signature sig : msg1Sigs){ - if (!msg2Sigs.contains(sig)) { - return -1; - } - } - - return 0; - } -} +package meerkat.util; + +import meerkat.protobuf.BulletinBoardAPI; +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.protobuf.Crypto.*; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +/** + * Created by Arbel Deutsch Peled on 05-Dec-15. + * This class implements a comparison between BulletinBoardMessage instances that disregards: + * 1. The entry number (since this can be different between database instances) + * 2. The order of the signatures + */ +public class BulletinBoardMessageComparator implements Comparator { + + /** + * Compare the messages + * @param msg1 + * @param msg2 + * @return 0 if the messages are equivalent (see above) and -1 otherwise. + */ + @Override + public int compare(BulletinBoardMessage msg1, BulletinBoardMessage msg2) { + + + + // Compare Timestamps + + if (!msg1.getMsg().getTimestamp().equals(msg2.getMsg().getTimestamp())){ + return -1; + } + + // Compare tags (enforce order) + + List tags1 = msg1.getMsg().getTagList(); + Iterator tags2 = msg2.getMsg().getTagList().iterator(); + + for (String tag : tags1){ + if (!tags2.hasNext()) { + return -1; + } + if (!tags2.next().equals(tag)){ + return -1; + } + } + + // Compare data + + if (msg1.getMsg().getDataTypeCase() != msg2.getMsg().getDataTypeCase()){ + return -1; + } + + if (msg1.getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.DATA){ + if (!msg1.getMsg().getData().equals(msg2.getMsg().getData())){ + return -1; + } + } else if (msg1.getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.MSGID){ + if (!msg1.getMsg().getMsgId().equals(msg2.getMsg().getMsgId())){ + return -1; + } + } + + // Compare signatures (do not enforce order) + + List sigs1 = msg1.getSigList(); + List sigs2 = msg2.getSigList(); + + if (sigs1.size() != sigs2.size()){ + return -1; + } + + for (Signature sig : sigs1){ + if (!sigs2.contains(sig)) { + return -1; + } + } + + return 0; + } +} diff --git a/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageGenerator.java b/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageGenerator.java index dff562e..5969a1e 100644 --- a/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageGenerator.java +++ b/meerkat-common/src/main/java/meerkat/util/BulletinBoardMessageGenerator.java @@ -7,7 +7,6 @@ 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 +27,33 @@ public class BulletinBoardMessageGenerator { return (byte) random.nextInt(); } + private byte[] randomBytes(int length) { + + byte[] result = new byte[length]; + + for (int i = 0; i < length; i++) { + result[i] = randomByte(); + } + + return result; + } + private String randomString(){ return new BigInteger(130, random).toString(32); } + private List randomStrings(int length) { + + List result = new LinkedList<>(); + + for (int i = 0; i < length; i++) { + result.add(randomString()); + } + + return result; + + } + /** * Generates a complete instance of a BulletinBoardMessage * @param signers contains the (possibly multiple) credentials required to sign the message @@ -46,23 +68,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 +117,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 { diff --git a/meerkat-common/src/main/java/meerkat/util/BulletinBoardUtils.java b/meerkat-common/src/main/java/meerkat/util/BulletinBoardUtils.java index 8793b2d..a83f755 100644 --- a/meerkat-common/src/main/java/meerkat/util/BulletinBoardUtils.java +++ b/meerkat-common/src/main/java/meerkat/util/BulletinBoardUtils.java @@ -1,7 +1,10 @@ package meerkat.util; +import com.google.protobuf.ByteString; +import com.google.protobuf.Int64Value; import meerkat.protobuf.BulletinBoardAPI.*; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -104,4 +107,129 @@ public class BulletinBoardUtils { return new java.sql.Timestamp(protoTimestamp.getSeconds() * 1000 + protoTimestamp.getNanos() / 1000000); } + /** + * Breaks up a bulletin board message into chunks + * @param msg is the complete message + * @return a list of BatchChunks that contains the raw message data + */ + public static List breakToBatch(BulletinBoardMessage msg, int chunkSize) { + + byte[] data = msg.getMsg().getData().toByteArray(); + + int chunkNum = data.length / chunkSize; + if (data.length % chunkSize != 0) + chunkNum++; + + List chunkList = new ArrayList<>(chunkNum); + + int location = 0; + + for (int i=0 ; i < chunkNum ; i++) { + + int chunkLength; + + if (i == chunkNum - 1){ + chunkLength = data.length % chunkSize; + if (chunkLength == 0){ + chunkLength = chunkSize; + } + } else{ + chunkLength = chunkSize; + } + + chunkList.add(BatchChunk.newBuilder() + .setData(ByteString.copyFrom(data, location, chunkLength)) + .build()); + + location += chunkLength; + + } + + return chunkList; + + } + + /** + * Removes concrete data from the message and turns it into a stub + * Note that the stub does not contain the message ID + * Therefore, it cannot be used to retrieve the message from a server + * @param msg is the original message + * @return the message stub + */ + public static BulletinBoardMessage makeStub(BulletinBoardMessage msg) { + + return BulletinBoardMessage.newBuilder() + .mergeFrom(msg) + .setMsg(UnsignedBulletinBoardMessage.newBuilder() + .mergeFrom(msg.getMsg()) + .clearDataType() + .clearData() + .build()) + .build(); + + } + + /** + * Merges a batch chunk list back into a message stub to create a complete Bulletin Board message + * @param msgStub is a message stub + * @param chunkList contains the (ordered) data of the batch message + * @return a complete message containing both data and metadata + */ + public static BulletinBoardMessage gatherBatch(BulletinBoardMessage msgStub, List chunkList) { + + List dataList = new LinkedList<>(); + + for (BatchChunk chunk : chunkList){ + dataList.add(chunk.getData()); + } + + return BulletinBoardMessage.newBuilder() + .mergeFrom(msgStub) + .setMsg(UnsignedBulletinBoardMessage.newBuilder() + .mergeFrom(msgStub.getMsg()) + .setData(ByteString.copyFrom(dataList)) + .build()) + .build(); + + } + + /** + * Gerenates a BeginBatchMessage Protobuf which is used to begin uploading a message as a batch + * @param msg is the Bulletin Board message to be uploaded, which can be a stub or a complete message + * @return the required BeginBatchMessage + */ + public static BeginBatchMessage generateBeginBatchMessage(BulletinBoardMessage msg) { + + if (msg.getSigCount() <= 0){ + throw new IllegalArgumentException("No signatures found"); + } + + return BeginBatchMessage.newBuilder() + .addAllTag(msg.getMsg().getTagList()) + .build(); + + } + + /** + * Gerenates a CloseBatchMessage Protobuf which is used to finalize a batch message + * @param batchId is the temporary identifier for the message + * @param batchLength is the number of chunks in the batch + * @param msg is the Bulletin Board message that was uploaded (and can also be a stub of said message) + * @throws IllegalArgumentException if the message contains no signatures + */ + public static CloseBatchMessage generateCloseBatchMessage(Int64Value batchId, int batchLength, BulletinBoardMessage msg) { + + if (msg.getSigCount() <= 0){ + throw new IllegalArgumentException("No signatures found"); + } + + return CloseBatchMessage.newBuilder() + .setTimestamp(msg.getMsg().getTimestamp()) + .setBatchLength(batchLength) + .setBatchId(batchId.getValue()) + .addAllSig(msg.getSigList()) + .build(); + + } + } diff --git a/meerkat-common/src/main/java/meerkat/util/Hex.java b/meerkat-common/src/main/java/meerkat/util/Hex.java index 8064d7d..2d0e4ef 100644 --- a/meerkat-common/src/main/java/meerkat/util/Hex.java +++ b/meerkat-common/src/main/java/meerkat/util/Hex.java @@ -1,26 +1,26 @@ -package meerkat.util; - -import com.google.protobuf.ByteString; - -/** - * Convert to/from Hex - */ -public class Hex { - /** - * Encode a {@link ByteString} as a hex string. - * @param str - * @return - */ - public static String encode(ByteString str) { - StringBuilder s = new StringBuilder(); - for (byte b : str) { - s.append(Integer.toHexString(((int) b) & 0xff)); - } - return s.toString(); - } - - public static String encode(byte[] bytes) { - return encode(ByteString.copyFrom(bytes)); - } -} - +package meerkat.util; + +import com.google.protobuf.ByteString; + +/** + * Convert to/from Hex + */ +public class Hex { + /** + * Encode a {@link ByteString} as a hex string. + * @param str + * @return + */ + public static String encode(ByteString str) { + StringBuilder s = new StringBuilder(); + for (byte b : str) { + s.append(Integer.toHexString(((int) b) & 0xff)); + } + return s.toString(); + } + + public static String encode(byte[] bytes) { + return encode(ByteString.copyFrom(bytes)); + } +} + diff --git a/meerkat-common/src/main/java/meerkat/voting/VotingBooth.java b/meerkat-common/src/main/java/meerkat/voting/VotingBooth.java index 5f8a7cf..e337a83 100644 --- a/meerkat-common/src/main/java/meerkat/voting/VotingBooth.java +++ b/meerkat-common/src/main/java/meerkat/voting/VotingBooth.java @@ -1,70 +1,70 @@ -package meerkat.voting; - -import static meerkat.protobuf.Voting.*; - -/** - * Created by talm on 25/10/15. - */ -public interface VotingBooth { - - public interface UI { - /** - * Prepare UI for a new user. - */ - void votingBegin(); - - /** - * UI must physically commit to an encrypted (or Wombat style) ballot. - * (probably by printing) - * - * When commitment is complete, should ask voter to choose between - * cast and audit. - * - * Called by votingbooth thread. - */ - void commitToEncryptedBallot(EncryptedBallot ballot); - - - /** - * Finalize a vote for casting - * Called by votingbooth in case user decides to cast. - */ - void castVote(); - - /** - * Submit audit information and spoil vote. - * Called by votingbooth in case user decides to audit - * @param ballotSecrets - */ - void auditVote(BallotSecrets ballotSecrets); - - - } - - /** - * Must be called before using any other method. - * @param globalParams global election parameters (e.g., global signing key, global encryption key) - * @param boothParams local parameters (e.g., private signature key for booth, randomness table?) - */ - void init(ElectionParams globalParams, BoothParams boothParams); - - - /** - * Called from UI thread when voter has finished making selection. - * - * Should encrypt ballot and commit. - * @param ballot - */ - void submitBallot(PlaintextBallot ballot); - - /** - * UI calls this when the user cancels the voting process in the middle. - */ - void cancelBallot(); - - /** - * Called by UI thread after voter made choice to cast or audit ballot. - * @param castVote - */ - void voterCastOrAudit(boolean castVote); -} +package meerkat.voting; + +import static meerkat.protobuf.Voting.*; + +/** + * Created by talm on 25/10/15. + */ +public interface VotingBooth { + + public interface UI { + /** + * Prepare UI for a new user. + */ + void votingBegin(); + + /** + * UI must physically commit to an encrypted (or Wombat style) ballot. + * (probably by printing) + * + * When commitment is complete, should ask voter to choose between + * cast and audit. + * + * Called by votingbooth thread. + */ + void commitToEncryptedBallot(EncryptedBallot ballot); + + + /** + * Finalize a vote for casting + * Called by votingbooth in case user decides to cast. + */ + void castVote(); + + /** + * Submit audit information and spoil vote. + * Called by votingbooth in case user decides to audit + * @param ballotSecrets + */ + void auditVote(BallotSecrets ballotSecrets); + + + } + + /** + * Must be called before using any other method. + * @param globalParams global election parameters (e.g., global signing key, global encryption key) + * @param boothParams local parameters (e.g., private signature key for booth, randomness table?) + */ + void init(ElectionParams globalParams, BoothParams boothParams); + + + /** + * Called from UI thread when voter has finished making selection. + * + * Should encrypt ballot and commit. + * @param ballot + */ + void submitBallot(PlaintextBallot ballot); + + /** + * UI calls this when the user cancels the voting process in the middle. + */ + void cancelBallot(); + + /** + * Called by UI thread after voter made choice to cast or audit ballot. + * @param castVote + */ + void voterCastOrAudit(boolean castVote); +} diff --git a/meerkat-common/src/main/proto/meerkat/BulletinBoardAPI.proto b/meerkat-common/src/main/proto/meerkat/BulletinBoardAPI.proto index 27ad512..fd6ea3c 100644 --- a/meerkat-common/src/main/proto/meerkat/BulletinBoardAPI.proto +++ b/meerkat-common/src/main/proto/meerkat/BulletinBoardAPI.proto @@ -1,163 +1,174 @@ -syntax = "proto3"; - -package meerkat; - -option java_package = "meerkat.protobuf"; - -import 'meerkat/crypto.proto'; -import 'google/protobuf/timestamp.proto'; - -message MessageID { - // The ID of a message for unique retrieval. - // Note that it is assumed that this ID is a function of the message itself. - bytes ID = 1; -} - -message UnsignedBulletinBoardMessage { - // Optional tags describing message; Used for message retrieval - repeated string tag = 1; - - // Timestamp of the message (as defined by client) - google.protobuf.Timestamp timestamp = 2; - - // The actual content of the message - bytes data = 3; -} - -message BulletinBoardMessage { - - // Serial entry number of message in database - int64 entryNum = 1; - - // Unsigned raw data of message - UnsignedBulletinBoardMessage msg = 2; - - // Signature of message (and tags), excluding the entry number. - repeated meerkat.Signature sig = 3; - -} - -message BulletinBoardMessageList { - - repeated BulletinBoardMessage message = 1; - -} - -enum FilterType { - MSG_ID = 0; // Match exact message ID - EXACT_ENTRY = 1; // Match exact entry number in database (chronological) - MAX_ENTRY = 2; // Find all entries in database up to specified entry number (chronological) - MIN_ENTRY = 3; // Find all entries in database starting from specified entry number (chronological) - SIGNER_ID = 4; // Find all entries in database that correspond to specific signature (signer) - TAG = 5; // Find all entries in database that have a specific tag - AFTER_TIME = 6; // Find all entries in database that occurred on or after a given timestamp - BEFORE_TIME = 7; // Find all entries in database that occurred on or before a given timestamp - - // NOTE: The MAX_MESSAGES filter must remain the last filter type - // This is because the condition it specifies in an SQL statement must come last in the statement - // Keeping it last here allows for easily sorting the filters and keeping the code general - MAX_MESSAGES = 8; // Return at most some specified number of messages -} - -message MessageFilter { - - FilterType type = 1; - - oneof filter{ - bytes id = 2; - int64 entry = 3; - string tag = 4; - int64 maxMessages = 5; - google.protobuf.Timestamp timestamp = 6; - } -} - -message MessageFilterList { - - // Combination of filters. - // To be implemented using intersection ("AND") operations. - repeated MessageFilter filter = 1; - -} - -// This message is used to start a batch transfer to the Bulletin Board Server -message BeginBatchMessage { - bytes signerId = 1; // Unique signer identifier - int32 batchId = 2; // Unique identifier for the batch (unique per signer) - repeated string tag = 3; // Tags for the batch message -} - -// This message is used to finalize and sign a batch transfer to the Bulletin Board Server -message CloseBatchMessage { - int32 batchId = 1; // Unique identifier for the batch (unique per signer) - int32 batchLength = 2; // Number of messages in the batch - google.protobuf.Timestamp timestamp = 3; // Timestamp of the batch (as defined by client) - meerkat.Signature sig = 4; // Signature on the (ordered) batch messages -} - -// Container for single batch message data -message BatchData { - bytes data = 1; -} - -// List of BatchData; Only used for testing -message BatchDataList { - repeated BatchData data = 1; -} - -// These messages comprise a batch message -message BatchMessage { - bytes signerId = 1; // Unique signer identifier - int32 batchId = 2; // Unique identifier for the batch (unique per signer) - int32 serialNum = 3; // Location of the message in the batch: starting from 0 - BatchData data = 4; // Actual data -} - -// This message defines which batch to read and from which location to start reading -message BatchSpecificationMessage { - bytes signerId = 1; // Unique signer identifier - int32 batchId = 2; // Unique identifier for the batch (unique per signer) - int32 startPosition = 3; // Position in batch to start reading from -} - -// This message is used to define a single query to the server to ascertain whether or not the server is synched with the client -// up till a specified timestamp -message SingleSyncQuery { - - google.protobuf.Timestamp timeOfSync = 1; - int64 checksum = 2; - -} - -// This message defines a complete server sync query -message SyncQuery { - - MessageFilterList filterList = 1; - - repeated SingleSyncQuery query = 2; - -} - -// This message defines the required information for generation of a SyncQuery instance by the server -message GenerateSyncQueryParams { - - // Defines the set of messages required - MessageFilterList filterList = 1; - - // Defines the locations in the list of messages to calculate single sync queries for - // The values should be between 0.0 and 1.0 and define the location in fractions of the size of the message set - repeated float breakpointList = 2; - -} - -// This message defines the server's response format to a sync query -message SyncQueryResponse { - - // Serial entry number of current last entry in database - // Set to zero (0) in case no query checksums match - int64 lastEntryNum = 1; - - // Largest value of timestamp for which the checksums match - google.protobuf.Timestamp lastTimeOfSync = 2; - +syntax = "proto3"; + +package meerkat; + +option java_package = "meerkat.protobuf"; + +import 'meerkat/crypto.proto'; +import 'google/protobuf/timestamp.proto'; + +message MessageID { + // The ID of a message for unique retrieval. + // Note that it is assumed that this ID is a function of the message itself. + bytes ID = 1; +} + +message UnsignedBulletinBoardMessage { + // Optional tags describing message; Used for message retrieval + repeated string tag = 1; + + // Timestamp of the message (as defined by client) + google.protobuf.Timestamp timestamp = 2; + + // The payload of the message + oneof dataType{ + + // A unique message identifier + bytes msgId = 3; + + // The actual content of the message + bytes data = 4; + + } + + +} + +message BulletinBoardMessage { + + // Serial entry number of message in database + int64 entryNum = 1; + + // Unsigned raw data of message + UnsignedBulletinBoardMessage msg = 2; + + // Signature of message (and tags), excluding the entry number. + repeated meerkat.Signature sig = 3; + +} + +message BulletinBoardMessageList { + + repeated BulletinBoardMessage message = 1; + +} + +enum FilterType { + MSG_ID = 0; // Match exact message ID + EXACT_ENTRY = 1; // Match exact entry number in database (chronological) + MAX_ENTRY = 2; // Find all entries in database up to specified entry number (chronological) + MIN_ENTRY = 3; // Find all entries in database starting from specified entry number (chronological) + SIGNER_ID = 4; // Find all entries in database that correspond to specific signature (signer) + TAG = 5; // Find all entries in database that have a specific tag + AFTER_TIME = 6; // Find all entries in database that occurred on or after a given timestamp + BEFORE_TIME = 7; // Find all entries in database that occurred on or before a given timestamp + + // NOTE: The MAX_MESSAGES filter must remain the last filter type + // This is because the condition it specifies in an SQL statement must come last in the statement + // Keeping it last here allows for easily sorting the filters and keeping the code general + MAX_MESSAGES = 8; // Return at most some specified number of messages +} + +message MessageFilter { + + FilterType type = 1; + + oneof filter{ + bytes id = 2; + int64 entry = 3; + string tag = 4; + int64 maxMessages = 5; + google.protobuf.Timestamp timestamp = 6; + } +} + +message MessageFilterList { + + // Combination of filters. + // To be implemented using intersection ("AND") operations. + repeated MessageFilter filter = 1; + +} + +// This message is used to start a batch transfer to the Bulletin Board Server +message BeginBatchMessage { + repeated string tag = 1; // Tags for the batch message +} + +// This message is used to finalize and sign a batch transfer to the Bulletin Board Server +message CloseBatchMessage { + int64 batchId = 1; // Unique temporary identifier for the batch + int32 batchLength = 2; // Number of messages in the batch + google.protobuf.Timestamp timestamp = 3; // Timestamp of the batch (as defined by client) + repeated meerkat.Signature sig = 4; // Signatures on the (ordered) batch messages +} + +// Container for single chunk of abatch message +message BatchChunk { + bytes data = 1; +} + +// List of BatchChunk; Only used for testing +message BatchChunkList { + repeated BatchChunk data = 1; +} + +// These messages comprise a batch message +message BatchMessage { + int64 batchId = 1; // Unique temporary identifier for the batch + int32 serialNum = 2; // Location of the message in the batch: starting from 0 + BatchChunk data = 3; // Actual data +} + +// This message is used to define a single query to the server to ascertain whether or not the server is synched with the client +// up till a specified timestamp +message SingleSyncQuery { + + google.protobuf.Timestamp timeOfSync = 1; + int64 checksum = 2; + +} + +// This message defines a complete server sync query +message SyncQuery { + + MessageFilterList filterList = 1; + + repeated SingleSyncQuery query = 2; + +} + +// This message defines the required information for generation of a SyncQuery instance by the server +message GenerateSyncQueryParams { + + // Defines the set of messages required + MessageFilterList filterList = 1; + + // Defines the locations in the list of messages to calculate single sync queries for + // The values should be between 0.0 and 1.0 and define the location in fractions of the size of the message set + repeated float breakpointList = 2; + +} + +// This message defines the server's response format to a sync query +message SyncQueryResponse { + + // Serial entry number of current last entry in database + // Set to zero (0) in case no query checksums match + int64 lastEntryNum = 1; + + // Largest value of timestamp for which the checksums match + google.protobuf.Timestamp lastTimeOfSync = 2; + +} + +// This message defines a query for retrieval of batch data +message BatchQuery { + + // The unique message ID if the batch + MessageID msgID = 1; + + // The first chunk to retrieve (0 is the first chunk) + int32 startPosition = 2; + } \ No newline at end of file diff --git a/meerkat-common/src/main/proto/meerkat/concrete_crypto.proto b/meerkat-common/src/main/proto/meerkat/concrete_crypto.proto index 9f1f818..d8c40d3 100644 --- a/meerkat-common/src/main/proto/meerkat/concrete_crypto.proto +++ b/meerkat-common/src/main/proto/meerkat/concrete_crypto.proto @@ -1,22 +1,22 @@ -// Protobufs for specific crypto primitives - -syntax = "proto3"; - -package meerkat; - -import 'meerkat/crypto.proto'; - -option java_package = "meerkat.protobuf"; - - -message ElGamalPublicKey { - // DER-encoded SubjectPublicKeyInfo as in RFC 3279 - bytes subject_public_key_info = 1; -} - -// An El-Gamal ciphertext -// Each group element should be an ASN.1 encoded curve point with compression. -message ElGamalCiphertext { - bytes c1 = 1; // First group element - bytes c2 = 2; // Second group element +// Protobufs for specific crypto primitives + +syntax = "proto3"; + +package meerkat; + +import 'meerkat/crypto.proto'; + +option java_package = "meerkat.protobuf"; + + +message ElGamalPublicKey { + // DER-encoded SubjectPublicKeyInfo as in RFC 3279 + bytes subject_public_key_info = 1; +} + +// An El-Gamal ciphertext +// Each group element should be an ASN.1 encoded curve point with compression. +message ElGamalCiphertext { + bytes c1 = 1; // First group element + bytes c2 = 2; // Second group element } \ No newline at end of file diff --git a/meerkat-common/src/main/proto/meerkat/crypto.proto b/meerkat-common/src/main/proto/meerkat/crypto.proto index 3b3e906..eeec159 100644 --- a/meerkat-common/src/main/proto/meerkat/crypto.proto +++ b/meerkat-common/src/main/proto/meerkat/crypto.proto @@ -1,54 +1,54 @@ -syntax = "proto3"; - -package meerkat; - -option java_package = "meerkat.protobuf"; - -enum SignatureType { - ECDSA = 0; - DSA = 1; -} - -message BigInteger { - bytes data = 1; -} - -// A digital signature -message Signature { - SignatureType type = 1; - - // Data encoding depends on type; default is DER-encoded - bytes data = 2; - - // ID of the signer (should be the fingerprint of the signature verification key) - bytes signer_id = 3; -} - -// Public key used to verify signatures -message SignatureVerificationKey { - SignatureType type = 1; - - // Data encoding depends on type; default is x509 DER-encoded - bytes data = 2; -} - -// A public encryption key -message EncryptionPublicKey { - bytes data = 1; -} - -// Randomness used for encryption -message EncryptionRandomness { - bytes data = 1; -} - -// A proof that randomness is correctly generated -message RandomnessGenerationProof { - bytes data = 1; -} - -// An encrypted message (rerandomizable) -message RerandomizableEncryptedMessage { - bytes data = 1; -} - +syntax = "proto3"; + +package meerkat; + +option java_package = "meerkat.protobuf"; + +enum SignatureType { + ECDSA = 0; + DSA = 1; +} + +message BigInteger { + bytes data = 1; +} + +// A digital signature +message Signature { + SignatureType type = 1; + + // Data encoding depends on type; default is DER-encoded + bytes data = 2; + + // ID of the signer (should be the fingerprint of the signature verification key) + bytes signer_id = 3; +} + +// Public key used to verify signatures +message SignatureVerificationKey { + SignatureType type = 1; + + // Data encoding depends on type; default is x509 DER-encoded + bytes data = 2; +} + +// A public encryption key +message EncryptionPublicKey { + bytes data = 1; +} + +// Randomness used for encryption +message EncryptionRandomness { + bytes data = 1; +} + +// A proof that randomness is correctly generated +message RandomnessGenerationProof { + bytes data = 1; +} + +// An encrypted message (rerandomizable) +message RerandomizableEncryptedMessage { + bytes data = 1; +} + diff --git a/meerkat-common/src/main/proto/meerkat/mixing.proto b/meerkat-common/src/main/proto/meerkat/mixing.proto index 2d5d6da..50cffc7 100644 --- a/meerkat-common/src/main/proto/meerkat/mixing.proto +++ b/meerkat-common/src/main/proto/meerkat/mixing.proto @@ -1,12 +1,12 @@ -syntax = "proto3"; - -package meerkat; - -option java_package = "meerkat.protobuf"; - -import 'meerkat/crypto.proto'; - -// TODO: -message ZeroKnowledgeProof { - bytes data = 1; +syntax = "proto3"; + +package meerkat; + +option java_package = "meerkat.protobuf"; + +import 'meerkat/crypto.proto'; + +// TODO: +message ZeroKnowledgeProof { + bytes data = 1; } \ No newline at end of file diff --git a/meerkat-common/src/main/proto/meerkat/voting.proto b/meerkat-common/src/main/proto/meerkat/voting.proto index f539ab8..9837cce 100644 --- a/meerkat-common/src/main/proto/meerkat/voting.proto +++ b/meerkat-common/src/main/proto/meerkat/voting.proto @@ -1,90 +1,90 @@ -syntax = "proto3"; - -package meerkat; - -import 'meerkat/crypto.proto'; - -option java_package = "meerkat.protobuf"; - -// A ballot question. This is an opaque -// data type that is parsed by the UI to display -// the question. -message BallotQuestion { - bytes data = 1; -} - -// An answer to a specific ballot question. -// The answer is a vector of signed integers, -// to encompass voting schemes such as ranked voting -// and STV. -message BallotAnswer { - repeated sint64 answer = 1 [packed=true]; -} - -message PlaintextBallot { - uint64 serialNumber = 1; // Ballot serial number - - repeated BallotAnswer answers = 2; -} - -message EncryptedBallot { - uint64 serialNumber = 1; // Ballot serial number - - RerandomizableEncryptedMessage data = 2; -} - -message BallotSecrets { - PlaintextBallot plaintext_ballot = 1; - - EncryptionRandomness encryption_randomness = 2; - RandomnessGenerationProof proof = 3; -} - -message BoothParams { - repeated SignatureVerificationKey pscVerificationKeys = 1; - -} - -// A table to translate to and from compactly encoded answers -// and their human-understandable counterparts. -// This should be parsable by the UI -message BallotAnswerTranslationTable { - bytes data = 1; -} - -// Data required in order to access the Bulletin Board Servers -message BulletinBoardClientParams { - - // Addresses of all Bulletin Board Servers - repeated string bulletinBoardAddress = 1; - - // Threshold fraction of successful servers posts before a post task is considered complete - float minRedundancy = 2; -} - -message ElectionParams { - // TODO: different sets of keys for different roles? - repeated SignatureVerificationKey trusteeVerificationKeys = 1; - - // How many trustees must participate in a signature for it to be considered valid. - uint32 trusteeSignatureThreshold = 2; - - // The key used to encrypt ballots. The corresponding private key - // is shared between the trustees. - EncryptionPublicKey ballotEncryptionKey = 3; - - // Verification keys for valid mixers. - repeated SignatureVerificationKey mixerVerificationKeys = 4; - - // How many mixers must participate for the mixing to be considered valid - uint32 mixerThreshold = 5; - - // Candidate list (or other question format) - repeated BallotQuestion questions = 6; - - // Translation table between answers and plaintext encoding - BallotAnswerTranslationTable answerTranslationTable = 7; - - // Data required in order to access the Bulletin Board Servers - BulletinBoardClientParams bulletinBoardClientParams = 8; -} +syntax = "proto3"; + +package meerkat; + +import 'meerkat/crypto.proto'; + +option java_package = "meerkat.protobuf"; + +// A ballot question. This is an opaque +// data type that is parsed by the UI to display +// the question. +message BallotQuestion { + bytes data = 1; +} + +// An answer to a specific ballot question. +// The answer is a vector of signed integers, +// to encompass voting schemes such as ranked voting +// and STV. +message BallotAnswer { + repeated sint64 answer = 1 [packed=true]; +} + +message PlaintextBallot { + uint64 serialNumber = 1; // Ballot serial number + + repeated BallotAnswer answers = 2; +} + +message EncryptedBallot { + uint64 serialNumber = 1; // Ballot serial number + + RerandomizableEncryptedMessage data = 2; +} + +message BallotSecrets { + PlaintextBallot plaintext_ballot = 1; + + EncryptionRandomness encryption_randomness = 2; + RandomnessGenerationProof proof = 3; +} + +message BoothParams { + repeated SignatureVerificationKey pscVerificationKeys = 1; + +} + +// A table to translate to and from compactly encoded answers +// and their human-understandable counterparts. +// This should be parsable by the UI +message BallotAnswerTranslationTable { + bytes data = 1; +} + +// Data required in order to access the Bulletin Board Servers +message BulletinBoardClientParams { + + // Addresses of all Bulletin Board Servers + repeated string bulletinBoardAddress = 1; + + // Threshold fraction of successful servers posts before a post task is considered complete + float minRedundancy = 2; +} + +message ElectionParams { + // TODO: different sets of keys for different roles? + repeated SignatureVerificationKey trusteeVerificationKeys = 1; + + // How many trustees must participate in a signature for it to be considered valid. + uint32 trusteeSignatureThreshold = 2; + + // The key used to encrypt ballots. The corresponding private key + // is shared between the trustees. + EncryptionPublicKey ballotEncryptionKey = 3; + + // Verification keys for valid mixers. + repeated SignatureVerificationKey mixerVerificationKeys = 4; + + // How many mixers must participate for the mixing to be considered valid + uint32 mixerThreshold = 5; + + // Candidate list (or other question format) + repeated BallotQuestion questions = 6; + + // Translation table between answers and plaintext encoding + BallotAnswerTranslationTable answerTranslationTable = 7; + + // Data required in order to access the Bulletin Board Servers + BulletinBoardClientParams bulletinBoardClientParams = 8; +} diff --git a/meerkat-common/src/main/resources/logback.groovy b/meerkat-common/src/main/resources/logback.groovy index 85fa80d..076b6c4 100644 --- a/meerkat-common/src/main/resources/logback.groovy +++ b/meerkat-common/src/main/resources/logback.groovy @@ -1,46 +1,46 @@ - - -import ch.qos.logback.classic.encoder.PatternLayoutEncoder -import ch.qos.logback.classic.filter.ThresholdFilter -import ch.qos.logback.core.ConsoleAppender -import ch.qos.logback.core.util.Duration -import static ch.qos.logback.classic.Level.* - -if (System.getProperty("log.debug") != null) { - println "Logback configuration debugging enabled" - - statusListener(OnConsoleStatusListener) -} - -def LOG_LEVEL = toLevel(System.getProperty("log.level"), INFO) - -def haveBeagle = System.getProperty("log.beagle") != null -def logOps = System.getProperty("log.ops") != null - -appender("CONSOLE", ConsoleAppender) { - - filter(ThresholdFilter) { - level = toLevel(System.getProperty("log.level"), TRACE) - } - - encoder(PatternLayoutEncoder) { - pattern = "%d{HH:mm:ss.SSS} [%thread %file:%line] %-5level %logger{0} - %msg%n" - } -} - -def appenders = [ "CONSOLE" ] - -if (haveBeagle) { - appender("SOCKET", SocketAppender) { - includeCallerData = true - remoteHost = "localhost" - port = 4321 - reconnectionDelay = new Duration(10000) - } - - appenders += ["SOCKET"] -} - -root(LOG_LEVEL, appenders) - - + + +import ch.qos.logback.classic.encoder.PatternLayoutEncoder +import ch.qos.logback.classic.filter.ThresholdFilter +import ch.qos.logback.core.ConsoleAppender +import ch.qos.logback.core.util.Duration +import static ch.qos.logback.classic.Level.* + +if (System.getProperty("log.debug") != null) { + println "Logback configuration debugging enabled" + + statusListener(OnConsoleStatusListener) +} + +def LOG_LEVEL = toLevel(System.getProperty("log.level"), INFO) + +def haveBeagle = System.getProperty("log.beagle") != null +def logOps = System.getProperty("log.ops") != null + +appender("CONSOLE", ConsoleAppender) { + + filter(ThresholdFilter) { + level = toLevel(System.getProperty("log.level"), TRACE) + } + + encoder(PatternLayoutEncoder) { + pattern = "%d{HH:mm:ss.SSS} [%thread %file:%line] %-5level %logger{0} - %msg%n" + } +} + +def appenders = [ "CONSOLE" ] + +if (haveBeagle) { + appender("SOCKET", SocketAppender) { + includeCallerData = true + remoteHost = "localhost" + port = 4321 + reconnectionDelay = new Duration(10000) + } + + appenders += ["SOCKET"] +} + +root(LOG_LEVEL, appenders) + + diff --git a/meerkat-common/src/test/java/meerkat/bulletinboard/BulletinBoardDigestTest.java b/meerkat-common/src/test/java/meerkat/bulletinboard/BulletinBoardDigestTest.java new file mode 100644 index 0000000..277bda1 --- /dev/null +++ b/meerkat-common/src/test/java/meerkat/bulletinboard/BulletinBoardDigestTest.java @@ -0,0 +1,112 @@ +package meerkat.bulletinboard; + +import com.google.protobuf.ByteString; +import meerkat.crypto.concrete.ECDSASignature; +import meerkat.crypto.concrete.SHA256Digest; +import meerkat.protobuf.BulletinBoardAPI.*; +import meerkat.util.BulletinBoardMessageGenerator; +import meerkat.util.BulletinBoardUtils; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.security.*; +import java.security.cert.CertificateException; +import java.util.List; +import java.util.Random; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.fail; + +/** + * Created by Arbel Deutsch Peled on 16-Jun-16. + */ +public class BulletinBoardDigestTest { + + private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12"; + private static String KEYFILE_EXAMPLE3 = "/certs/enduser-certs/user3-key-with-password-shh.p12"; + + private static String KEYFILE_PASSWORD1 = "secret"; + private static String KEYFILE_PASSWORD3 = "shh"; + + private GenericBulletinBoardSignature[] signers; + private ByteString[] signerIDs; + + @Before + public void init() { + + signers = new GenericBulletinBoardSignature[2]; + signerIDs = new ByteString[signers.length]; + signers[0] = new GenericBulletinBoardSignature(new ECDSASignature()); + signers[1] = new GenericBulletinBoardSignature(new ECDSASignature()); + + InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE); + char[] password = KEYFILE_PASSWORD1.toCharArray(); + + KeyStore.Builder keyStoreBuilder = null; + try { + keyStoreBuilder = signers[0].getPKCS12KeyStoreBuilder(keyStream, password); + + signers[0].loadSigningCertificate(keyStoreBuilder); + + keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE3); + password = KEYFILE_PASSWORD3.toCharArray(); + + keyStoreBuilder = signers[1].getPKCS12KeyStoreBuilder(keyStream, password); + signers[1].loadSigningCertificate(keyStoreBuilder); + + for (int i = 0 ; i < signers.length ; i++) { + signerIDs[i] = signers[i].getSignerID(); + } + + } 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()); + } + + } + + @Test + public void testBatchDigest() throws SignatureException { + + final int MESSAGE_SIZE = 100; + final int CHUNK_SIZE = 10; + final int TAG_NUM = 10; + + BulletinBoardMessageGenerator generator = new BulletinBoardMessageGenerator(new Random(0)); + + BulletinBoardMessage completeMessage = generator.generateRandomMessage(signers, MESSAGE_SIZE, TAG_NUM); + + BulletinBoardMessage stub = BulletinBoardUtils.makeStub(completeMessage); + List batchChunks = BulletinBoardUtils.breakToBatch(completeMessage, CHUNK_SIZE); + + BulletinBoardDigest digest = new GenericBulletinBoardDigest(new SHA256Digest()); + + digest.update(completeMessage); + MessageID id1 = digest.digestAsMessageID(); + + digest.update(stub); + for (BatchChunk batchChunk : batchChunks){ + digest.update(batchChunk.getData().toByteArray()); + } + + MessageID id2 = digest.digestAsMessageID(); + + assertThat("Digests not equal!", id1.getID().equals(id2.getID())); + + } + +} diff --git a/meerkat-common/src/test/resources/certs/README.md b/meerkat-common/src/test/resources/certs/README.md index ecb74d0..f7283d9 100644 --- a/meerkat-common/src/test/resources/certs/README.md +++ b/meerkat-common/src/test/resources/certs/README.md @@ -1,6 +1,6 @@ -Certs and private keys for testing generated using OpenSSL - -.crt and .pem files are in PEM format -.der files are in binary DER format - -files that have a name of the form *-with-password-xxxx.pem are encrypted with the password xxxx +Certs and private keys for testing generated using OpenSSL + +.crt and .pem files are in PEM format +.der files are in binary DER format + +files that have a name of the form *-with-password-xxxx.pem are encrypted with the password xxxx diff --git a/meerkat-common/src/test/resources/certs/enduser-certs/user1-key-with-password-secret.pem b/meerkat-common/src/test/resources/certs/enduser-certs/user1-key-with-password-secret.pem index dccf58d..e859995 100644 --- a/meerkat-common/src/test/resources/certs/enduser-certs/user1-key-with-password-secret.pem +++ b/meerkat-common/src/test/resources/certs/enduser-certs/user1-key-with-password-secret.pem @@ -1,8 +1,8 @@ ------BEGIN EC PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: AES-256-CBC,243D718A0D80C59590E582A26E87A49C - -RG6ITUTIdbJdWYX57oMn3tTCzHJSTjXAIZLjoVxy/v4UFYjluaFhGonIlbH1q2pP -ueu29Q3eT6144ypB8ARUJ1x0kRX1OL9zNHgdF9ulrCf9/nhGyC2nL+tHZ0YPbxoQ -+6yCQcRWvjUXLVzPEUnwMuHXJDpaXES8X0R4CISQKIA= ------END EC PRIVATE KEY----- +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,243D718A0D80C59590E582A26E87A49C + +RG6ITUTIdbJdWYX57oMn3tTCzHJSTjXAIZLjoVxy/v4UFYjluaFhGonIlbH1q2pP +ueu29Q3eT6144ypB8ARUJ1x0kRX1OL9zNHgdF9ulrCf9/nhGyC2nL+tHZ0YPbxoQ ++6yCQcRWvjUXLVzPEUnwMuHXJDpaXES8X0R4CISQKIA= +-----END EC PRIVATE KEY----- diff --git a/meerkat-common/src/test/resources/certs/enduser-certs/user1-key.pem b/meerkat-common/src/test/resources/certs/enduser-certs/user1-key.pem index 239bd21..6619e37 100644 --- a/meerkat-common/src/test/resources/certs/enduser-certs/user1-key.pem +++ b/meerkat-common/src/test/resources/certs/enduser-certs/user1-key.pem @@ -1,5 +1,5 @@ ------BEGIN PRIVATE KEY----- -MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQge8JqCoaLoZq61aQki5Xm -GppcfAAkhHDGNQw/wLof5LmhRANCAAQJD1kW6BsNkRY9tslaugpOJOaoKX4uBz4S -Q96lPaPWkatNVgQchwNeB/hdjZwNuwE7A7XAwr69HFmhXRhsM005 ------END PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQge8JqCoaLoZq61aQki5Xm +GppcfAAkhHDGNQw/wLof5LmhRANCAAQJD1kW6BsNkRY9tslaugpOJOaoKX4uBz4S +Q96lPaPWkatNVgQchwNeB/hdjZwNuwE7A7XAwr69HFmhXRhsM005 +-----END PRIVATE KEY----- diff --git a/meerkat-common/src/test/resources/certs/enduser-certs/user1-pubkey.pem b/meerkat-common/src/test/resources/certs/enduser-certs/user1-pubkey.pem index 4724631..1c0a0c1 100644 --- a/meerkat-common/src/test/resources/certs/enduser-certs/user1-pubkey.pem +++ b/meerkat-common/src/test/resources/certs/enduser-certs/user1-pubkey.pem @@ -1,4 +1,4 @@ ------BEGIN PUBLIC KEY----- -MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECQ9ZFugbDZEWPbbJWroKTiTmqCl+Lgc+ -EkPepT2j1pGrTVYEHIcDXgf4XY2cDbsBOwO1wMK+vRxZoV0YbDNNOQ== ------END PUBLIC KEY----- +-----BEGIN PUBLIC KEY----- +MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECQ9ZFugbDZEWPbbJWroKTiTmqCl+Lgc+ +EkPepT2j1pGrTVYEHIcDXgf4XY2cDbsBOwO1wMK+vRxZoV0YbDNNOQ== +-----END PUBLIC KEY----- diff --git a/meerkat-common/src/test/resources/certs/enduser-certs/user1.crt b/meerkat-common/src/test/resources/certs/enduser-certs/user1.crt index e3d4843..d80093f 100644 --- a/meerkat-common/src/test/resources/certs/enduser-certs/user1.crt +++ b/meerkat-common/src/test/resources/certs/enduser-certs/user1.crt @@ -1,19 +1,19 @@ ------BEGIN CERTIFICATE----- -MIIDFjCCArygAwIBAgICEAAwCgYIKoZIzj0EAwIwgYIxKTAnBgNVBAMMIE1lZXJr -YXQgVm90aW5nIEludGVybWVkaWF0ZSBDQSAxMRMwEQYDVQQIDApTb21lLVN0YXRl -MQswCQYDVQQGEwJJTDEVMBMGA1UECgwMSURDIEhlcnpsaXlhMRwwGgYDVQQLDBNN -ZWVya2F0IFZvdGluZyBUZWFtMB4XDTE1MTExMTE2MTM1NFoXDTI1MTEwODE2MTM1 -NFowbjEaMBgGA1UEAwwRUG9sbGluZyBTdGF0aW9uIDExEzARBgNVBAgMClNvbWUt -U3RhdGUxCzAJBgNVBAYTAklMMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV -BAsMDk1lZXJrYXQgVm90aW5nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECQ9ZFugb -DZEWPbbJWroKTiTmqCl+Lgc+EkPepT2j1pGrTVYEHIcDXgf4XY2cDbsBOwO1wMK+ -vRxZoV0YbDNNOaOCATYwggEyMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFLamS8o2 -hFNd0vWy/irEBNWVNwFXMB8GA1UdIwQYMBaAFBeyv0c75eT6PNumHo9TZ2B9vtcp -MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATBEBgNVHR8EPTA7MDmg -N6A1hjNodHRwOi8vY3JsLmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRp -YXRlMS5jcmwwegYIKwYBBQUHAQEEbjBsMEEGCCsGAQUFBzAChjVodHRwOi8vcGtp -LmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRpYXRlLWNhLmNydDAnBggr -BgEFBQcwAYYbaHR0cDovL29jc3AuZmFjdGNlbnRlci5vcmcvMAoGCCqGSM49BAMC -A0gAMEUCIQD6QbhNNmB3AVVqhmXuiLA7WF6raShw6n0g/VloVGQebQIgEvxYclpO -MMynt5wH6X65rtn4Q1EGaDMvNbFweCDsldk= ------END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDFjCCArygAwIBAgICEAAwCgYIKoZIzj0EAwIwgYIxKTAnBgNVBAMMIE1lZXJr +YXQgVm90aW5nIEludGVybWVkaWF0ZSBDQSAxMRMwEQYDVQQIDApTb21lLVN0YXRl +MQswCQYDVQQGEwJJTDEVMBMGA1UECgwMSURDIEhlcnpsaXlhMRwwGgYDVQQLDBNN +ZWVya2F0IFZvdGluZyBUZWFtMB4XDTE1MTExMTE2MTM1NFoXDTI1MTEwODE2MTM1 +NFowbjEaMBgGA1UEAwwRUG9sbGluZyBTdGF0aW9uIDExEzARBgNVBAgMClNvbWUt +U3RhdGUxCzAJBgNVBAYTAklMMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV +BAsMDk1lZXJrYXQgVm90aW5nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECQ9ZFugb +DZEWPbbJWroKTiTmqCl+Lgc+EkPepT2j1pGrTVYEHIcDXgf4XY2cDbsBOwO1wMK+ +vRxZoV0YbDNNOaOCATYwggEyMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFLamS8o2 +hFNd0vWy/irEBNWVNwFXMB8GA1UdIwQYMBaAFBeyv0c75eT6PNumHo9TZ2B9vtcp +MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATBEBgNVHR8EPTA7MDmg +N6A1hjNodHRwOi8vY3JsLmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRp +YXRlMS5jcmwwegYIKwYBBQUHAQEEbjBsMEEGCCsGAQUFBzAChjVodHRwOi8vcGtp +LmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRpYXRlLWNhLmNydDAnBggr +BgEFBQcwAYYbaHR0cDovL29jc3AuZmFjdGNlbnRlci5vcmcvMAoGCCqGSM49BAMC +A0gAMEUCIQD6QbhNNmB3AVVqhmXuiLA7WF6raShw6n0g/VloVGQebQIgEvxYclpO +MMynt5wH6X65rtn4Q1EGaDMvNbFweCDsldk= +-----END CERTIFICATE----- diff --git a/meerkat-common/src/test/resources/certs/enduser-certs/user1.csr b/meerkat-common/src/test/resources/certs/enduser-certs/user1.csr index 0f59d09..20e1efc 100644 --- a/meerkat-common/src/test/resources/certs/enduser-certs/user1.csr +++ b/meerkat-common/src/test/resources/certs/enduser-certs/user1.csr @@ -1,9 +1,9 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIBOjCB4QIBADCBgTELMAkGA1UEBhMCSUwxEzARBgNVBAgMClNvbWUtU3RhdGUx -ETAPBgNVBAcMCEhlcnpsaXlhMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV -BAsMDk1lZXJrYXQgVm90aW5nMRowGAYDVQQDDBFQb2xsaW5nIFN0YXRpb24gMTBW -MBAGByqGSM49AgEGBSuBBAAKA0IABAkPWRboGw2RFj22yVq6Ck4k5qgpfi4HPhJD -3qU9o9aRq01WBByHA14H+F2NnA27ATsDtcDCvr0cWaFdGGwzTTmgADAKBggqhkjO -PQQDAgNIADBFAiEA8gmIhALr7O5M1QLReGH3jheildTIr1mDWl14WyMf9U4CIF23 -mInyo4VqNHLzxMLg5Cn3Oddokng3OXa63y4nTfv+ ------END CERTIFICATE REQUEST----- +-----BEGIN CERTIFICATE REQUEST----- +MIIBOjCB4QIBADCBgTELMAkGA1UEBhMCSUwxEzARBgNVBAgMClNvbWUtU3RhdGUx +ETAPBgNVBAcMCEhlcnpsaXlhMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV +BAsMDk1lZXJrYXQgVm90aW5nMRowGAYDVQQDDBFQb2xsaW5nIFN0YXRpb24gMTBW +MBAGByqGSM49AgEGBSuBBAAKA0IABAkPWRboGw2RFj22yVq6Ck4k5qgpfi4HPhJD +3qU9o9aRq01WBByHA14H+F2NnA27ATsDtcDCvr0cWaFdGGwzTTmgADAKBggqhkjO +PQQDAgNIADBFAiEA8gmIhALr7O5M1QLReGH3jheildTIr1mDWl14WyMf9U4CIF23 +mInyo4VqNHLzxMLg5Cn3Oddokng3OXa63y4nTfv+ +-----END CERTIFICATE REQUEST----- diff --git a/meerkat-common/src/test/resources/certs/enduser-certs/user2-key.pem b/meerkat-common/src/test/resources/certs/enduser-certs/user2-key.pem index 1967905..2d31bb8 100644 --- a/meerkat-common/src/test/resources/certs/enduser-certs/user2-key.pem +++ b/meerkat-common/src/test/resources/certs/enduser-certs/user2-key.pem @@ -1,5 +1,5 @@ ------BEGIN PRIVATE KEY----- -MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgYpBEO+XWm/n6VPeMVK76 -mrZkDTpiwLsDykG7M4fU5RKhRANCAAR71/kVGyA3hdxcLBBT3NPQF6R3LholmLRN -qhnvHqzJWuy7ev+Xbuxtt9AN0ajyeFDy8Oe1bUSidnLyQi+nXC0f ------END PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgYpBEO+XWm/n6VPeMVK76 +mrZkDTpiwLsDykG7M4fU5RKhRANCAAR71/kVGyA3hdxcLBBT3NPQF6R3LholmLRN +qhnvHqzJWuy7ev+Xbuxtt9AN0ajyeFDy8Oe1bUSidnLyQi+nXC0f +-----END PRIVATE KEY----- diff --git a/meerkat-common/src/test/resources/certs/enduser-certs/user2-pubkey.pem b/meerkat-common/src/test/resources/certs/enduser-certs/user2-pubkey.pem index 72f7acf..5d86d4c 100644 --- a/meerkat-common/src/test/resources/certs/enduser-certs/user2-pubkey.pem +++ b/meerkat-common/src/test/resources/certs/enduser-certs/user2-pubkey.pem @@ -1,4 +1,4 @@ ------BEGIN PUBLIC KEY----- -MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEe9f5FRsgN4XcXCwQU9zT0Bekdy4aJZi0 -TaoZ7x6syVrsu3r/l27sbbfQDdGo8nhQ8vDntW1EonZy8kIvp1wtHw== ------END PUBLIC KEY----- +-----BEGIN PUBLIC KEY----- +MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEe9f5FRsgN4XcXCwQU9zT0Bekdy4aJZi0 +TaoZ7x6syVrsu3r/l27sbbfQDdGo8nhQ8vDntW1EonZy8kIvp1wtHw== +-----END PUBLIC KEY----- diff --git a/meerkat-common/src/test/resources/certs/enduser-certs/user2.crt b/meerkat-common/src/test/resources/certs/enduser-certs/user2.crt index e618261..a211365 100644 --- a/meerkat-common/src/test/resources/certs/enduser-certs/user2.crt +++ b/meerkat-common/src/test/resources/certs/enduser-certs/user2.crt @@ -1,19 +1,19 @@ ------BEGIN CERTIFICATE----- -MIIDFjCCArygAwIBAgICEAEwCgYIKoZIzj0EAwIwgYIxKTAnBgNVBAMMIE1lZXJr -YXQgVm90aW5nIEludGVybWVkaWF0ZSBDQSAxMRMwEQYDVQQIDApTb21lLVN0YXRl -MQswCQYDVQQGEwJJTDEVMBMGA1UECgwMSURDIEhlcnpsaXlhMRwwGgYDVQQLDBNN -ZWVya2F0IFZvdGluZyBUZWFtMB4XDTE1MTExMTE2MjAzM1oXDTI1MTEwODE2MjAz -M1owbjEaMBgGA1UEAwwRUG9sbGluZyBTdGF0aW9uIDIxEzARBgNVBAgMClNvbWUt -U3RhdGUxCzAJBgNVBAYTAklMMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV -BAsMDk1lZXJrYXQgVm90aW5nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEe9f5FRsg -N4XcXCwQU9zT0Bekdy4aJZi0TaoZ7x6syVrsu3r/l27sbbfQDdGo8nhQ8vDntW1E -onZy8kIvp1wtH6OCATYwggEyMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKCdquYj -DGHqAHt+4PIDlw0h2UvuMB8GA1UdIwQYMBaAFBeyv0c75eT6PNumHo9TZ2B9vtcp -MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATBEBgNVHR8EPTA7MDmg -N6A1hjNodHRwOi8vY3JsLmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRp -YXRlMS5jcmwwegYIKwYBBQUHAQEEbjBsMEEGCCsGAQUFBzAChjVodHRwOi8vcGtp -LmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRpYXRlLWNhLmNydDAnBggr -BgEFBQcwAYYbaHR0cDovL29jc3AuZmFjdGNlbnRlci5vcmcvMAoGCCqGSM49BAMC -A0gAMEUCIQDpo5B0vvEJSax3YzOMfE8l0pfDUIKLdBWJVGeq0VLtIgIgVr0+4/0e -n+R+l1OVOLh2GirloOgbv5Ch5BQ2pQNAG2Y= ------END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDFjCCArygAwIBAgICEAEwCgYIKoZIzj0EAwIwgYIxKTAnBgNVBAMMIE1lZXJr +YXQgVm90aW5nIEludGVybWVkaWF0ZSBDQSAxMRMwEQYDVQQIDApTb21lLVN0YXRl +MQswCQYDVQQGEwJJTDEVMBMGA1UECgwMSURDIEhlcnpsaXlhMRwwGgYDVQQLDBNN +ZWVya2F0IFZvdGluZyBUZWFtMB4XDTE1MTExMTE2MjAzM1oXDTI1MTEwODE2MjAz +M1owbjEaMBgGA1UEAwwRUG9sbGluZyBTdGF0aW9uIDIxEzARBgNVBAgMClNvbWUt +U3RhdGUxCzAJBgNVBAYTAklMMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV +BAsMDk1lZXJrYXQgVm90aW5nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEe9f5FRsg +N4XcXCwQU9zT0Bekdy4aJZi0TaoZ7x6syVrsu3r/l27sbbfQDdGo8nhQ8vDntW1E +onZy8kIvp1wtH6OCATYwggEyMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKCdquYj +DGHqAHt+4PIDlw0h2UvuMB8GA1UdIwQYMBaAFBeyv0c75eT6PNumHo9TZ2B9vtcp +MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATBEBgNVHR8EPTA7MDmg +N6A1hjNodHRwOi8vY3JsLmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRp +YXRlMS5jcmwwegYIKwYBBQUHAQEEbjBsMEEGCCsGAQUFBzAChjVodHRwOi8vcGtp +LmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRpYXRlLWNhLmNydDAnBggr +BgEFBQcwAYYbaHR0cDovL29jc3AuZmFjdGNlbnRlci5vcmcvMAoGCCqGSM49BAMC +A0gAMEUCIQDpo5B0vvEJSax3YzOMfE8l0pfDUIKLdBWJVGeq0VLtIgIgVr0+4/0e +n+R+l1OVOLh2GirloOgbv5Ch5BQ2pQNAG2Y= +-----END CERTIFICATE----- diff --git a/meerkat-common/src/test/resources/certs/enduser-certs/user2.csr b/meerkat-common/src/test/resources/certs/enduser-certs/user2.csr index 774a0d3..bb3c2d0 100644 --- a/meerkat-common/src/test/resources/certs/enduser-certs/user2.csr +++ b/meerkat-common/src/test/resources/certs/enduser-certs/user2.csr @@ -1,9 +1,9 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIBOzCB4QIBADCBgTELMAkGA1UEBhMCSUwxEzARBgNVBAgMClNvbWUtU3RhdGUx -ETAPBgNVBAcMCEhlcnpsaXlhMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV -BAsMDk1lZXJrYXQgVm90aW5nMRowGAYDVQQDDBFQb2xsaW5nIFN0YXRpb24gMjBW -MBAGByqGSM49AgEGBSuBBAAKA0IABHvX+RUbIDeF3FwsEFPc09AXpHcuGiWYtE2q -Ge8erMla7Lt6/5du7G230A3RqPJ4UPLw57VtRKJ2cvJCL6dcLR+gADAKBggqhkjO -PQQDAgNJADBGAiEA6Ls/ojRaZT+u4YeOBYcPbRcJE3jSTe1Sm/lR7fDyEhMCIQCk -UOca+e2b8+CqM3CURBv6TqUMmZ3HeMRvEAxFPqOWSw== ------END CERTIFICATE REQUEST----- +-----BEGIN CERTIFICATE REQUEST----- +MIIBOzCB4QIBADCBgTELMAkGA1UEBhMCSUwxEzARBgNVBAgMClNvbWUtU3RhdGUx +ETAPBgNVBAcMCEhlcnpsaXlhMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV +BAsMDk1lZXJrYXQgVm90aW5nMRowGAYDVQQDDBFQb2xsaW5nIFN0YXRpb24gMjBW +MBAGByqGSM49AgEGBSuBBAAKA0IABHvX+RUbIDeF3FwsEFPc09AXpHcuGiWYtE2q +Ge8erMla7Lt6/5du7G230A3RqPJ4UPLw57VtRKJ2cvJCL6dcLR+gADAKBggqhkjO +PQQDAgNJADBGAiEA6Ls/ojRaZT+u4YeOBYcPbRcJE3jSTe1Sm/lR7fDyEhMCIQCk +UOca+e2b8+CqM3CURBv6TqUMmZ3HeMRvEAxFPqOWSw== +-----END CERTIFICATE REQUEST----- diff --git a/meerkat-common/src/test/resources/certs/intermediate-ca-1/1000.pem b/meerkat-common/src/test/resources/certs/intermediate-ca-1/1000.pem index e3d4843..d80093f 100644 --- a/meerkat-common/src/test/resources/certs/intermediate-ca-1/1000.pem +++ b/meerkat-common/src/test/resources/certs/intermediate-ca-1/1000.pem @@ -1,19 +1,19 @@ ------BEGIN CERTIFICATE----- -MIIDFjCCArygAwIBAgICEAAwCgYIKoZIzj0EAwIwgYIxKTAnBgNVBAMMIE1lZXJr -YXQgVm90aW5nIEludGVybWVkaWF0ZSBDQSAxMRMwEQYDVQQIDApTb21lLVN0YXRl -MQswCQYDVQQGEwJJTDEVMBMGA1UECgwMSURDIEhlcnpsaXlhMRwwGgYDVQQLDBNN -ZWVya2F0IFZvdGluZyBUZWFtMB4XDTE1MTExMTE2MTM1NFoXDTI1MTEwODE2MTM1 -NFowbjEaMBgGA1UEAwwRUG9sbGluZyBTdGF0aW9uIDExEzARBgNVBAgMClNvbWUt -U3RhdGUxCzAJBgNVBAYTAklMMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV -BAsMDk1lZXJrYXQgVm90aW5nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECQ9ZFugb -DZEWPbbJWroKTiTmqCl+Lgc+EkPepT2j1pGrTVYEHIcDXgf4XY2cDbsBOwO1wMK+ -vRxZoV0YbDNNOaOCATYwggEyMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFLamS8o2 -hFNd0vWy/irEBNWVNwFXMB8GA1UdIwQYMBaAFBeyv0c75eT6PNumHo9TZ2B9vtcp -MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATBEBgNVHR8EPTA7MDmg -N6A1hjNodHRwOi8vY3JsLmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRp -YXRlMS5jcmwwegYIKwYBBQUHAQEEbjBsMEEGCCsGAQUFBzAChjVodHRwOi8vcGtp -LmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRpYXRlLWNhLmNydDAnBggr -BgEFBQcwAYYbaHR0cDovL29jc3AuZmFjdGNlbnRlci5vcmcvMAoGCCqGSM49BAMC -A0gAMEUCIQD6QbhNNmB3AVVqhmXuiLA7WF6raShw6n0g/VloVGQebQIgEvxYclpO -MMynt5wH6X65rtn4Q1EGaDMvNbFweCDsldk= ------END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDFjCCArygAwIBAgICEAAwCgYIKoZIzj0EAwIwgYIxKTAnBgNVBAMMIE1lZXJr +YXQgVm90aW5nIEludGVybWVkaWF0ZSBDQSAxMRMwEQYDVQQIDApTb21lLVN0YXRl +MQswCQYDVQQGEwJJTDEVMBMGA1UECgwMSURDIEhlcnpsaXlhMRwwGgYDVQQLDBNN +ZWVya2F0IFZvdGluZyBUZWFtMB4XDTE1MTExMTE2MTM1NFoXDTI1MTEwODE2MTM1 +NFowbjEaMBgGA1UEAwwRUG9sbGluZyBTdGF0aW9uIDExEzARBgNVBAgMClNvbWUt +U3RhdGUxCzAJBgNVBAYTAklMMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV +BAsMDk1lZXJrYXQgVm90aW5nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECQ9ZFugb +DZEWPbbJWroKTiTmqCl+Lgc+EkPepT2j1pGrTVYEHIcDXgf4XY2cDbsBOwO1wMK+ +vRxZoV0YbDNNOaOCATYwggEyMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFLamS8o2 +hFNd0vWy/irEBNWVNwFXMB8GA1UdIwQYMBaAFBeyv0c75eT6PNumHo9TZ2B9vtcp +MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATBEBgNVHR8EPTA7MDmg +N6A1hjNodHRwOi8vY3JsLmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRp +YXRlMS5jcmwwegYIKwYBBQUHAQEEbjBsMEEGCCsGAQUFBzAChjVodHRwOi8vcGtp +LmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRpYXRlLWNhLmNydDAnBggr +BgEFBQcwAYYbaHR0cDovL29jc3AuZmFjdGNlbnRlci5vcmcvMAoGCCqGSM49BAMC +A0gAMEUCIQD6QbhNNmB3AVVqhmXuiLA7WF6raShw6n0g/VloVGQebQIgEvxYclpO +MMynt5wH6X65rtn4Q1EGaDMvNbFweCDsldk= +-----END CERTIFICATE----- diff --git a/meerkat-common/src/test/resources/certs/intermediate-ca-1/1001.pem b/meerkat-common/src/test/resources/certs/intermediate-ca-1/1001.pem index e618261..a211365 100644 --- a/meerkat-common/src/test/resources/certs/intermediate-ca-1/1001.pem +++ b/meerkat-common/src/test/resources/certs/intermediate-ca-1/1001.pem @@ -1,19 +1,19 @@ ------BEGIN CERTIFICATE----- -MIIDFjCCArygAwIBAgICEAEwCgYIKoZIzj0EAwIwgYIxKTAnBgNVBAMMIE1lZXJr -YXQgVm90aW5nIEludGVybWVkaWF0ZSBDQSAxMRMwEQYDVQQIDApTb21lLVN0YXRl -MQswCQYDVQQGEwJJTDEVMBMGA1UECgwMSURDIEhlcnpsaXlhMRwwGgYDVQQLDBNN -ZWVya2F0IFZvdGluZyBUZWFtMB4XDTE1MTExMTE2MjAzM1oXDTI1MTEwODE2MjAz -M1owbjEaMBgGA1UEAwwRUG9sbGluZyBTdGF0aW9uIDIxEzARBgNVBAgMClNvbWUt -U3RhdGUxCzAJBgNVBAYTAklMMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV -BAsMDk1lZXJrYXQgVm90aW5nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEe9f5FRsg -N4XcXCwQU9zT0Bekdy4aJZi0TaoZ7x6syVrsu3r/l27sbbfQDdGo8nhQ8vDntW1E -onZy8kIvp1wtH6OCATYwggEyMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKCdquYj -DGHqAHt+4PIDlw0h2UvuMB8GA1UdIwQYMBaAFBeyv0c75eT6PNumHo9TZ2B9vtcp -MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATBEBgNVHR8EPTA7MDmg -N6A1hjNodHRwOi8vY3JsLmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRp -YXRlMS5jcmwwegYIKwYBBQUHAQEEbjBsMEEGCCsGAQUFBzAChjVodHRwOi8vcGtp -LmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRpYXRlLWNhLmNydDAnBggr -BgEFBQcwAYYbaHR0cDovL29jc3AuZmFjdGNlbnRlci5vcmcvMAoGCCqGSM49BAMC -A0gAMEUCIQDpo5B0vvEJSax3YzOMfE8l0pfDUIKLdBWJVGeq0VLtIgIgVr0+4/0e -n+R+l1OVOLh2GirloOgbv5Ch5BQ2pQNAG2Y= ------END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDFjCCArygAwIBAgICEAEwCgYIKoZIzj0EAwIwgYIxKTAnBgNVBAMMIE1lZXJr +YXQgVm90aW5nIEludGVybWVkaWF0ZSBDQSAxMRMwEQYDVQQIDApTb21lLVN0YXRl +MQswCQYDVQQGEwJJTDEVMBMGA1UECgwMSURDIEhlcnpsaXlhMRwwGgYDVQQLDBNN +ZWVya2F0IFZvdGluZyBUZWFtMB4XDTE1MTExMTE2MjAzM1oXDTI1MTEwODE2MjAz +M1owbjEaMBgGA1UEAwwRUG9sbGluZyBTdGF0aW9uIDIxEzARBgNVBAgMClNvbWUt +U3RhdGUxCzAJBgNVBAYTAklMMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV +BAsMDk1lZXJrYXQgVm90aW5nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEe9f5FRsg +N4XcXCwQU9zT0Bekdy4aJZi0TaoZ7x6syVrsu3r/l27sbbfQDdGo8nhQ8vDntW1E +onZy8kIvp1wtH6OCATYwggEyMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKCdquYj +DGHqAHt+4PIDlw0h2UvuMB8GA1UdIwQYMBaAFBeyv0c75eT6PNumHo9TZ2B9vtcp +MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATBEBgNVHR8EPTA7MDmg +N6A1hjNodHRwOi8vY3JsLmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRp +YXRlMS5jcmwwegYIKwYBBQUHAQEEbjBsMEEGCCsGAQUFBzAChjVodHRwOi8vcGtp +LmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRpYXRlLWNhLmNydDAnBggr +BgEFBQcwAYYbaHR0cDovL29jc3AuZmFjdGNlbnRlci5vcmcvMAoGCCqGSM49BAMC +A0gAMEUCIQDpo5B0vvEJSax3YzOMfE8l0pfDUIKLdBWJVGeq0VLtIgIgVr0+4/0e +n+R+l1OVOLh2GirloOgbv5Ch5BQ2pQNAG2Y= +-----END CERTIFICATE----- diff --git a/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex b/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex index 72b4dde..1cd80cf 100644 --- a/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex +++ b/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex @@ -1,2 +1,2 @@ -V 251108161354Z 1000 unknown /CN=Polling Station 1/ST=Some-State/C=IL/O=IDC Herzliya/OU=Meerkat Voting -V 251108162033Z 1001 unknown /CN=Polling Station 2/ST=Some-State/C=IL/O=IDC Herzliya/OU=Meerkat Voting +V 251108161354Z 1000 unknown /CN=Polling Station 1/ST=Some-State/C=IL/O=IDC Herzliya/OU=Meerkat Voting +V 251108162033Z 1001 unknown /CN=Polling Station 2/ST=Some-State/C=IL/O=IDC Herzliya/OU=Meerkat Voting diff --git a/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex.attr b/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex.attr index f77ac48..3a7e39e 100644 --- a/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex.attr +++ b/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex.attr @@ -1 +1 @@ -unique_subject = no +unique_subject = no diff --git a/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex.attr.old b/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex.attr.old index f77ac48..3a7e39e 100644 --- a/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex.attr.old +++ b/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex.attr.old @@ -1 +1 @@ -unique_subject = no +unique_subject = no diff --git a/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex.old b/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex.old index 60f290b..7dcd55e 100644 --- a/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex.old +++ b/meerkat-common/src/test/resources/certs/intermediate-ca-1/certindex.old @@ -1 +1 @@ -V 251108161354Z 1000 unknown /CN=Polling Station 1/ST=Some-State/C=IL/O=IDC Herzliya/OU=Meerkat Voting +V 251108161354Z 1000 unknown /CN=Polling Station 1/ST=Some-State/C=IL/O=IDC Herzliya/OU=Meerkat Voting diff --git a/meerkat-common/src/test/resources/certs/intermediate-ca-1/certserial b/meerkat-common/src/test/resources/certs/intermediate-ca-1/certserial index a9b8266..7d802a3 100644 --- a/meerkat-common/src/test/resources/certs/intermediate-ca-1/certserial +++ b/meerkat-common/src/test/resources/certs/intermediate-ca-1/certserial @@ -1 +1 @@ -1002 +1002 diff --git a/meerkat-common/src/test/resources/certs/intermediate-ca-1/certserial.old b/meerkat-common/src/test/resources/certs/intermediate-ca-1/certserial.old index ecc680e..dd11724 100644 --- a/meerkat-common/src/test/resources/certs/intermediate-ca-1/certserial.old +++ b/meerkat-common/src/test/resources/certs/intermediate-ca-1/certserial.old @@ -1 +1 @@ -1001 +1001 diff --git a/meerkat-common/src/test/resources/certs/intermediate-ca-1/crlnumber b/meerkat-common/src/test/resources/certs/intermediate-ca-1/crlnumber index d5b0eb5..83b33d2 100644 --- a/meerkat-common/src/test/resources/certs/intermediate-ca-1/crlnumber +++ b/meerkat-common/src/test/resources/certs/intermediate-ca-1/crlnumber @@ -1 +1 @@ -1000 +1000 diff --git a/meerkat-common/src/test/resources/certs/intermediate-ca-1/intermediate-ca-1-private-key.pem b/meerkat-common/src/test/resources/certs/intermediate-ca-1/intermediate-ca-1-private-key.pem index 5c678f2..cf0a641 100644 --- a/meerkat-common/src/test/resources/certs/intermediate-ca-1/intermediate-ca-1-private-key.pem +++ b/meerkat-common/src/test/resources/certs/intermediate-ca-1/intermediate-ca-1-private-key.pem @@ -1,5 +1,5 @@ ------BEGIN PRIVATE KEY----- -MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgJvMhOfcQfdK/42QlBbri -IYXLM/gVHq/yppOykDqB3s6hRANCAAQoShAtCGW5c9pk/4/sKN1qjCgDKngqJpba -kku6cIDqXDr+aHsl+/KdSHd46OI3fEynl+/Pc85wRsaY6Z7b1PdS ------END PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgJvMhOfcQfdK/42QlBbri +IYXLM/gVHq/yppOykDqB3s6hRANCAAQoShAtCGW5c9pk/4/sKN1qjCgDKngqJpba +kku6cIDqXDr+aHsl+/KdSHd46OI3fEynl+/Pc85wRsaY6Z7b1PdS +-----END PRIVATE KEY----- diff --git a/meerkat-common/src/test/resources/certs/intermediate-ca-1/intermediate-ca-1.crt b/meerkat-common/src/test/resources/certs/intermediate-ca-1/intermediate-ca-1.crt index dda53dc..751d8e7 100644 --- a/meerkat-common/src/test/resources/certs/intermediate-ca-1/intermediate-ca-1.crt +++ b/meerkat-common/src/test/resources/certs/intermediate-ca-1/intermediate-ca-1.crt @@ -1,21 +1,21 @@ ------BEGIN CERTIFICATE----- -MIIDfDCCAyGgAwIBAgICEAAwCgYIKoZIzj0EAwIwgbAxCzAJBgNVBAYTAklMMRMw -EQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQHDAhIZXJ6bGl5YTEUMBIGA1UECgwL -SURDIEhlcmxpeWExHzAdBgNVBAsMFk1lZXJrYXQgVm90aW5nIFByb2plY3QxGDAW -BgNVBAMMD1Rlc3RpbmcgUm9vdCBDQTEoMCYGCSqGSIb3DQEJARYZdGVzdGluZy1j -YUBmYWN0Y2VudGVyLm9yZzAeFw0xNTExMTExNjA4MDJaFw0yNTExMDgxNjA4MDJa -MIGCMSkwJwYDVQQDDCBNZWVya2F0IFZvdGluZyBJbnRlcm1lZGlhdGUgQ0EgMTET -MBEGA1UECAwKU29tZS1TdGF0ZTELMAkGA1UEBhMCSUwxFTATBgNVBAoMDElEQyBI -ZXJ6bGl5YTEcMBoGA1UECwwTTWVlcmthdCBWb3RpbmcgVGVhbTBWMBAGByqGSM49 -AgEGBSuBBAAKA0IABChKEC0IZblz2mT/j+wo3WqMKAMqeComltqSS7pwgOpcOv5o -eyX78p1Id3jo4jd8TKeX789zznBGxpjpntvU91KjggFYMIIBVDAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBQXsr9HO+Xk+jzbph6PU2dgfb7XKTAfBgNVHSMEGDAW -gBSJD9L1fLmX4A9CBoLsYXn3OPy1ojALBgNVHQ8EBAMCAaYwEwYDVR0lBAwwCgYI -KwYBBQUHAwEwPgYDVR0fBDcwNTAzoDGgL4YtaHR0cDovL2NybC5mYWN0Y2VudGVy -Lm9yZy9tZWVya2F0LXJvb3QtY2EuY3JsMCsGA1UdEQQkMCKCIE1lZXJrYXQgVm90 -aW5nIEludGVybWVkaWF0ZSBDQSAxMHIGCCsGAQUFBwEBBGYwZDA5BggrBgEFBQcw -AoYtaHR0cDovL3BraS5mYWN0Y2VudGVyLm9yZy9tZWVya2F0LXJvb3QtY2EuY3J0 -MCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcC5mYWN0Y2VudGVyLm9yZy8wCgYIKoZI -zj0EAwIDSQAwRgIhALEMHq2ssC9rLXiG8v6NcZetwwxdu3B3LW9s0KeGoNIEAiEA -skA56tMnhiZe38msyanRyRrAHyBI2fGs6GP3UBrg2P8= ------END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDfDCCAyGgAwIBAgICEAAwCgYIKoZIzj0EAwIwgbAxCzAJBgNVBAYTAklMMRMw +EQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQHDAhIZXJ6bGl5YTEUMBIGA1UECgwL +SURDIEhlcmxpeWExHzAdBgNVBAsMFk1lZXJrYXQgVm90aW5nIFByb2plY3QxGDAW +BgNVBAMMD1Rlc3RpbmcgUm9vdCBDQTEoMCYGCSqGSIb3DQEJARYZdGVzdGluZy1j +YUBmYWN0Y2VudGVyLm9yZzAeFw0xNTExMTExNjA4MDJaFw0yNTExMDgxNjA4MDJa +MIGCMSkwJwYDVQQDDCBNZWVya2F0IFZvdGluZyBJbnRlcm1lZGlhdGUgQ0EgMTET +MBEGA1UECAwKU29tZS1TdGF0ZTELMAkGA1UEBhMCSUwxFTATBgNVBAoMDElEQyBI +ZXJ6bGl5YTEcMBoGA1UECwwTTWVlcmthdCBWb3RpbmcgVGVhbTBWMBAGByqGSM49 +AgEGBSuBBAAKA0IABChKEC0IZblz2mT/j+wo3WqMKAMqeComltqSS7pwgOpcOv5o +eyX78p1Id3jo4jd8TKeX789zznBGxpjpntvU91KjggFYMIIBVDAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBQXsr9HO+Xk+jzbph6PU2dgfb7XKTAfBgNVHSMEGDAW +gBSJD9L1fLmX4A9CBoLsYXn3OPy1ojALBgNVHQ8EBAMCAaYwEwYDVR0lBAwwCgYI +KwYBBQUHAwEwPgYDVR0fBDcwNTAzoDGgL4YtaHR0cDovL2NybC5mYWN0Y2VudGVy +Lm9yZy9tZWVya2F0LXJvb3QtY2EuY3JsMCsGA1UdEQQkMCKCIE1lZXJrYXQgVm90 +aW5nIEludGVybWVkaWF0ZSBDQSAxMHIGCCsGAQUFBwEBBGYwZDA5BggrBgEFBQcw +AoYtaHR0cDovL3BraS5mYWN0Y2VudGVyLm9yZy9tZWVya2F0LXJvb3QtY2EuY3J0 +MCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcC5mYWN0Y2VudGVyLm9yZy8wCgYIKoZI +zj0EAwIDSQAwRgIhALEMHq2ssC9rLXiG8v6NcZetwwxdu3B3LW9s0KeGoNIEAiEA +skA56tMnhiZe38msyanRyRrAHyBI2fGs6GP3UBrg2P8= +-----END CERTIFICATE----- diff --git a/meerkat-common/src/test/resources/certs/intermediate-ca-1/intermediate-ca-1.csr b/meerkat-common/src/test/resources/certs/intermediate-ca-1/intermediate-ca-1.csr index 55f66c1..fce3021 100644 --- a/meerkat-common/src/test/resources/certs/intermediate-ca-1/intermediate-ca-1.csr +++ b/meerkat-common/src/test/resources/certs/intermediate-ca-1/intermediate-ca-1.csr @@ -1,10 +1,10 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIBTTCB9QIBADCBlTELMAkGA1UEBhMCSUwxEzARBgNVBAgMClNvbWUtU3RhdGUx -ETAPBgNVBAcMCEhlcnpsaXlhMRUwEwYDVQQKDAxJREMgSGVyemxpeWExHDAaBgNV -BAsME01lZXJrYXQgVm90aW5nIFRlYW0xKTAnBgNVBAMMIE1lZXJrYXQgVm90aW5n -IEludGVybWVkaWF0ZSBDQSAxMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEKEoQLQhl -uXPaZP+P7CjdaowoAyp4KiaW2pJLunCA6lw6/mh7JfvynUh3eOjiN3xMp5fvz3PO -cEbGmOme29T3UqAAMAoGCCqGSM49BAMCA0cAMEQCIFlyJO5NFqnMUu5hOlQa872E -yy0V3zkqeN6Aly+LtEQqAiAfHwbi1lkJOZT2tOX8gfJzcac2jKmbgIhmITNq7uma -Wg== ------END CERTIFICATE REQUEST----- +-----BEGIN CERTIFICATE REQUEST----- +MIIBTTCB9QIBADCBlTELMAkGA1UEBhMCSUwxEzARBgNVBAgMClNvbWUtU3RhdGUx +ETAPBgNVBAcMCEhlcnpsaXlhMRUwEwYDVQQKDAxJREMgSGVyemxpeWExHDAaBgNV +BAsME01lZXJrYXQgVm90aW5nIFRlYW0xKTAnBgNVBAMMIE1lZXJrYXQgVm90aW5n +IEludGVybWVkaWF0ZSBDQSAxMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEKEoQLQhl +uXPaZP+P7CjdaowoAyp4KiaW2pJLunCA6lw6/mh7JfvynUh3eOjiN3xMp5fvz3PO +cEbGmOme29T3UqAAMAoGCCqGSM49BAMCA0cAMEQCIFlyJO5NFqnMUu5hOlQa872E +yy0V3zkqeN6Aly+LtEQqAiAfHwbi1lkJOZT2tOX8gfJzcac2jKmbgIhmITNq7uma +Wg== +-----END CERTIFICATE REQUEST----- diff --git a/meerkat-common/src/test/resources/certs/intermediate-ca-1/openssl-intermediate-ca.conf b/meerkat-common/src/test/resources/certs/intermediate-ca-1/openssl-intermediate-ca.conf index aa44c33..090ca1a 100644 --- a/meerkat-common/src/test/resources/certs/intermediate-ca-1/openssl-intermediate-ca.conf +++ b/meerkat-common/src/test/resources/certs/intermediate-ca-1/openssl-intermediate-ca.conf @@ -1,46 +1,46 @@ -[ ca ] -default_ca = myca - -[ crl_ext ] -issuerAltName=issuer:copy -authorityKeyIdentifier=keyid:always - - [ myca ] - dir = ./ - new_certs_dir = $dir - unique_subject = no - certificate = $dir/intermediate-ca-1.crt - database = $dir/certindex - private_key = $dir/intermediate-ca-1-private-key.pem - serial = $dir/certserial - default_days = 3650 - default_md = sha256 - policy = myca_policy - x509_extensions = myca_extensions - crlnumber = $dir/crlnumber - default_crl_days = 3650 - - [ myca_policy ] - commonName = supplied - stateOrProvinceName = optional - countryName = optional - emailAddress = optional - organizationName = supplied - organizationalUnitName = optional - - [ myca_extensions ] - basicConstraints = critical,CA:FALSE - keyUsage = critical,any - subjectKeyIdentifier = hash - authorityKeyIdentifier = keyid:always,issuer - keyUsage = digitalSignature,keyEncipherment - extendedKeyUsage = serverAuth - crlDistributionPoints = @crl_section - authorityInfoAccess = @ocsp_section - - [crl_section] - URI.0 = http://crl.factcenter.org/meerkat-intermediate1.crl - - [ocsp_section] - caIssuers;URI.0 = http://pki.factcenter.org/meerkat-intermediate-ca.crt - OCSP;URI.0 = http://ocsp.factcenter.org/ +[ ca ] +default_ca = myca + +[ crl_ext ] +issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always + + [ myca ] + dir = ./ + new_certs_dir = $dir + unique_subject = no + certificate = $dir/intermediate-ca-1.crt + database = $dir/certindex + private_key = $dir/intermediate-ca-1-private-key.pem + serial = $dir/certserial + default_days = 3650 + default_md = sha256 + policy = myca_policy + x509_extensions = myca_extensions + crlnumber = $dir/crlnumber + default_crl_days = 3650 + + [ myca_policy ] + commonName = supplied + stateOrProvinceName = optional + countryName = optional + emailAddress = optional + organizationName = supplied + organizationalUnitName = optional + + [ myca_extensions ] + basicConstraints = critical,CA:FALSE + keyUsage = critical,any + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer + keyUsage = digitalSignature,keyEncipherment + extendedKeyUsage = serverAuth + crlDistributionPoints = @crl_section + authorityInfoAccess = @ocsp_section + + [crl_section] + URI.0 = http://crl.factcenter.org/meerkat-intermediate1.crl + + [ocsp_section] + caIssuers;URI.0 = http://pki.factcenter.org/meerkat-intermediate-ca.crt + OCSP;URI.0 = http://ocsp.factcenter.org/ diff --git a/meerkat-common/src/test/resources/certs/root-ca/1000.pem b/meerkat-common/src/test/resources/certs/root-ca/1000.pem index dda53dc..751d8e7 100644 --- a/meerkat-common/src/test/resources/certs/root-ca/1000.pem +++ b/meerkat-common/src/test/resources/certs/root-ca/1000.pem @@ -1,21 +1,21 @@ ------BEGIN CERTIFICATE----- -MIIDfDCCAyGgAwIBAgICEAAwCgYIKoZIzj0EAwIwgbAxCzAJBgNVBAYTAklMMRMw -EQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQHDAhIZXJ6bGl5YTEUMBIGA1UECgwL -SURDIEhlcmxpeWExHzAdBgNVBAsMFk1lZXJrYXQgVm90aW5nIFByb2plY3QxGDAW -BgNVBAMMD1Rlc3RpbmcgUm9vdCBDQTEoMCYGCSqGSIb3DQEJARYZdGVzdGluZy1j -YUBmYWN0Y2VudGVyLm9yZzAeFw0xNTExMTExNjA4MDJaFw0yNTExMDgxNjA4MDJa -MIGCMSkwJwYDVQQDDCBNZWVya2F0IFZvdGluZyBJbnRlcm1lZGlhdGUgQ0EgMTET -MBEGA1UECAwKU29tZS1TdGF0ZTELMAkGA1UEBhMCSUwxFTATBgNVBAoMDElEQyBI -ZXJ6bGl5YTEcMBoGA1UECwwTTWVlcmthdCBWb3RpbmcgVGVhbTBWMBAGByqGSM49 -AgEGBSuBBAAKA0IABChKEC0IZblz2mT/j+wo3WqMKAMqeComltqSS7pwgOpcOv5o -eyX78p1Id3jo4jd8TKeX789zznBGxpjpntvU91KjggFYMIIBVDAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBQXsr9HO+Xk+jzbph6PU2dgfb7XKTAfBgNVHSMEGDAW -gBSJD9L1fLmX4A9CBoLsYXn3OPy1ojALBgNVHQ8EBAMCAaYwEwYDVR0lBAwwCgYI -KwYBBQUHAwEwPgYDVR0fBDcwNTAzoDGgL4YtaHR0cDovL2NybC5mYWN0Y2VudGVy -Lm9yZy9tZWVya2F0LXJvb3QtY2EuY3JsMCsGA1UdEQQkMCKCIE1lZXJrYXQgVm90 -aW5nIEludGVybWVkaWF0ZSBDQSAxMHIGCCsGAQUFBwEBBGYwZDA5BggrBgEFBQcw -AoYtaHR0cDovL3BraS5mYWN0Y2VudGVyLm9yZy9tZWVya2F0LXJvb3QtY2EuY3J0 -MCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcC5mYWN0Y2VudGVyLm9yZy8wCgYIKoZI -zj0EAwIDSQAwRgIhALEMHq2ssC9rLXiG8v6NcZetwwxdu3B3LW9s0KeGoNIEAiEA -skA56tMnhiZe38msyanRyRrAHyBI2fGs6GP3UBrg2P8= ------END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDfDCCAyGgAwIBAgICEAAwCgYIKoZIzj0EAwIwgbAxCzAJBgNVBAYTAklMMRMw +EQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQHDAhIZXJ6bGl5YTEUMBIGA1UECgwL +SURDIEhlcmxpeWExHzAdBgNVBAsMFk1lZXJrYXQgVm90aW5nIFByb2plY3QxGDAW +BgNVBAMMD1Rlc3RpbmcgUm9vdCBDQTEoMCYGCSqGSIb3DQEJARYZdGVzdGluZy1j +YUBmYWN0Y2VudGVyLm9yZzAeFw0xNTExMTExNjA4MDJaFw0yNTExMDgxNjA4MDJa +MIGCMSkwJwYDVQQDDCBNZWVya2F0IFZvdGluZyBJbnRlcm1lZGlhdGUgQ0EgMTET +MBEGA1UECAwKU29tZS1TdGF0ZTELMAkGA1UEBhMCSUwxFTATBgNVBAoMDElEQyBI +ZXJ6bGl5YTEcMBoGA1UECwwTTWVlcmthdCBWb3RpbmcgVGVhbTBWMBAGByqGSM49 +AgEGBSuBBAAKA0IABChKEC0IZblz2mT/j+wo3WqMKAMqeComltqSS7pwgOpcOv5o +eyX78p1Id3jo4jd8TKeX789zznBGxpjpntvU91KjggFYMIIBVDAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBQXsr9HO+Xk+jzbph6PU2dgfb7XKTAfBgNVHSMEGDAW +gBSJD9L1fLmX4A9CBoLsYXn3OPy1ojALBgNVHQ8EBAMCAaYwEwYDVR0lBAwwCgYI +KwYBBQUHAwEwPgYDVR0fBDcwNTAzoDGgL4YtaHR0cDovL2NybC5mYWN0Y2VudGVy +Lm9yZy9tZWVya2F0LXJvb3QtY2EuY3JsMCsGA1UdEQQkMCKCIE1lZXJrYXQgVm90 +aW5nIEludGVybWVkaWF0ZSBDQSAxMHIGCCsGAQUFBwEBBGYwZDA5BggrBgEFBQcw +AoYtaHR0cDovL3BraS5mYWN0Y2VudGVyLm9yZy9tZWVya2F0LXJvb3QtY2EuY3J0 +MCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcC5mYWN0Y2VudGVyLm9yZy8wCgYIKoZI +zj0EAwIDSQAwRgIhALEMHq2ssC9rLXiG8v6NcZetwwxdu3B3LW9s0KeGoNIEAiEA +skA56tMnhiZe38msyanRyRrAHyBI2fGs6GP3UBrg2P8= +-----END CERTIFICATE----- diff --git a/meerkat-common/src/test/resources/certs/root-ca/certindex b/meerkat-common/src/test/resources/certs/root-ca/certindex index ec8e90d..e4550ca 100644 --- a/meerkat-common/src/test/resources/certs/root-ca/certindex +++ b/meerkat-common/src/test/resources/certs/root-ca/certindex @@ -1 +1 @@ -V 251108160802Z 1000 unknown /CN=Meerkat Voting Intermediate CA 1/ST=Some-State/C=IL/O=IDC Herzliya/OU=Meerkat Voting Team +V 251108160802Z 1000 unknown /CN=Meerkat Voting Intermediate CA 1/ST=Some-State/C=IL/O=IDC Herzliya/OU=Meerkat Voting Team diff --git a/meerkat-common/src/test/resources/certs/root-ca/certindex.attr b/meerkat-common/src/test/resources/certs/root-ca/certindex.attr index f77ac48..3a7e39e 100644 --- a/meerkat-common/src/test/resources/certs/root-ca/certindex.attr +++ b/meerkat-common/src/test/resources/certs/root-ca/certindex.attr @@ -1 +1 @@ -unique_subject = no +unique_subject = no diff --git a/meerkat-common/src/test/resources/certs/root-ca/certserial b/meerkat-common/src/test/resources/certs/root-ca/certserial index ecc680e..dd11724 100644 --- a/meerkat-common/src/test/resources/certs/root-ca/certserial +++ b/meerkat-common/src/test/resources/certs/root-ca/certserial @@ -1 +1 @@ -1001 +1001 diff --git a/meerkat-common/src/test/resources/certs/root-ca/certserial.old b/meerkat-common/src/test/resources/certs/root-ca/certserial.old index d5b0eb5..83b33d2 100644 --- a/meerkat-common/src/test/resources/certs/root-ca/certserial.old +++ b/meerkat-common/src/test/resources/certs/root-ca/certserial.old @@ -1 +1 @@ -1000 +1000 diff --git a/meerkat-common/src/test/resources/certs/root-ca/crlnumber b/meerkat-common/src/test/resources/certs/root-ca/crlnumber index d5b0eb5..83b33d2 100644 --- a/meerkat-common/src/test/resources/certs/root-ca/crlnumber +++ b/meerkat-common/src/test/resources/certs/root-ca/crlnumber @@ -1 +1 @@ -1000 +1000 diff --git a/meerkat-common/src/test/resources/certs/root-ca/openssl-ca.conf b/meerkat-common/src/test/resources/certs/root-ca/openssl-ca.conf index e94692e..39e8b00 100644 --- a/meerkat-common/src/test/resources/certs/root-ca/openssl-ca.conf +++ b/meerkat-common/src/test/resources/certs/root-ca/openssl-ca.conf @@ -1,61 +1,61 @@ -[ ca ] -default_ca = myca - -[ crl_ext ] -issuerAltName=issuer:copy -authorityKeyIdentifier=keyid:always - - [ myca ] - dir = ./ - new_certs_dir = $dir - unique_subject = no - certificate = $dir/root-ca.crt - database = $dir/certindex - private_key = $dir/root-ca-private-key.pem - serial = $dir/certserial - default_days = 3650 - default_md = sha256 - policy = myca_policy - x509_extensions = myca_extensions - crlnumber = $dir/crlnumber - default_crl_days = 3650 - - [ myca_policy ] - commonName = supplied - stateOrProvinceName = optional - countryName = optional - emailAddress = optional - organizationName = supplied - organizationalUnitName = optional - - [ myca_extensions ] - basicConstraints = critical,CA:TRUE - keyUsage = critical,any - subjectKeyIdentifier = hash - authorityKeyIdentifier = keyid:always,issuer - keyUsage = digitalSignature,keyEncipherment,cRLSign,keyCertSign - extendedKeyUsage = serverAuth - crlDistributionPoints = @crl_section - subjectAltName = @alt_names - authorityInfoAccess = @ocsp_section - - [ v3_ca ] - basicConstraints = critical,CA:TRUE,pathlen:0 - keyUsage = critical,any - subjectKeyIdentifier = hash - authorityKeyIdentifier = keyid:always,issuer - keyUsage = digitalSignature,keyEncipherment,cRLSign,keyCertSign - extendedKeyUsage = serverAuth - crlDistributionPoints = @crl_section - subjectAltName = @alt_names - authorityInfoAccess = @ocsp_section - - [alt_names] - DNS.0 = Meerkat Voting Intermediate CA 1 - - [crl_section] - URI.0 = http://crl.factcenter.org/meerkat-root-ca.crl - - [ocsp_section] - caIssuers;URI.0 = http://pki.factcenter.org/meerkat-root-ca.crt - OCSP;URI.0 = http://ocsp.factcenter.org/ +[ ca ] +default_ca = myca + +[ crl_ext ] +issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always + + [ myca ] + dir = ./ + new_certs_dir = $dir + unique_subject = no + certificate = $dir/root-ca.crt + database = $dir/certindex + private_key = $dir/root-ca-private-key.pem + serial = $dir/certserial + default_days = 3650 + default_md = sha256 + policy = myca_policy + x509_extensions = myca_extensions + crlnumber = $dir/crlnumber + default_crl_days = 3650 + + [ myca_policy ] + commonName = supplied + stateOrProvinceName = optional + countryName = optional + emailAddress = optional + organizationName = supplied + organizationalUnitName = optional + + [ myca_extensions ] + basicConstraints = critical,CA:TRUE + keyUsage = critical,any + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer + keyUsage = digitalSignature,keyEncipherment,cRLSign,keyCertSign + extendedKeyUsage = serverAuth + crlDistributionPoints = @crl_section + subjectAltName = @alt_names + authorityInfoAccess = @ocsp_section + + [ v3_ca ] + basicConstraints = critical,CA:TRUE,pathlen:0 + keyUsage = critical,any + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer + keyUsage = digitalSignature,keyEncipherment,cRLSign,keyCertSign + extendedKeyUsage = serverAuth + crlDistributionPoints = @crl_section + subjectAltName = @alt_names + authorityInfoAccess = @ocsp_section + + [alt_names] + DNS.0 = Meerkat Voting Intermediate CA 1 + + [crl_section] + URI.0 = http://crl.factcenter.org/meerkat-root-ca.crl + + [ocsp_section] + caIssuers;URI.0 = http://pki.factcenter.org/meerkat-root-ca.crt + OCSP;URI.0 = http://ocsp.factcenter.org/ diff --git a/meerkat-common/src/test/resources/certs/root-ca/root-ca-private-key-with-password-secret.pem b/meerkat-common/src/test/resources/certs/root-ca/root-ca-private-key-with-password-secret.pem index 2801557..a0c442d 100644 --- a/meerkat-common/src/test/resources/certs/root-ca/root-ca-private-key-with-password-secret.pem +++ b/meerkat-common/src/test/resources/certs/root-ca/root-ca-private-key-with-password-secret.pem @@ -1,8 +1,8 @@ ------BEGIN EC PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: AES-256-CBC,B8CA131346FD6C9568A6C80935F2AF14 - -8q1seEln39/tQTo5KqN+qNRhd0fQ0oC71dYpfTHsP0NlNmjMtwKo2niFwzjxnSyP -vpJjGzUlnq30ucbeJA7CDm/1cmYAU5gGQ7gldgpi2TQVS+EBjqi/Y5P9AlrgLv6K -tKe4AvkqQcpi4ZvlUL9xmNaM9jEH4syopR9YClEMfa8= ------END EC PRIVATE KEY----- +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,B8CA131346FD6C9568A6C80935F2AF14 + +8q1seEln39/tQTo5KqN+qNRhd0fQ0oC71dYpfTHsP0NlNmjMtwKo2niFwzjxnSyP +vpJjGzUlnq30ucbeJA7CDm/1cmYAU5gGQ7gldgpi2TQVS+EBjqi/Y5P9AlrgLv6K +tKe4AvkqQcpi4ZvlUL9xmNaM9jEH4syopR9YClEMfa8= +-----END EC PRIVATE KEY----- diff --git a/meerkat-common/src/test/resources/certs/root-ca/root-ca-private-key.pem b/meerkat-common/src/test/resources/certs/root-ca/root-ca-private-key.pem index 7176b2a..7966a28 100644 --- a/meerkat-common/src/test/resources/certs/root-ca/root-ca-private-key.pem +++ b/meerkat-common/src/test/resources/certs/root-ca/root-ca-private-key.pem @@ -1,5 +1,5 @@ ------BEGIN EC PRIVATE KEY----- -MHQCAQEEIEi9y6pSKu1kDZcIfQQAnojl1iFxm32W0DVCp2P6HRrkoAcGBSuBBAAK -oUQDQgAEoijIYF12bpA0tcjyQnWZGQ4lzdBGR+hK/5al/M+zFgFwvWHoWf6yJsSB -ymviB5yUaH+cE+/3LXlGbpRzYKLBYQ== ------END EC PRIVATE KEY----- +-----BEGIN EC PRIVATE KEY----- +MHQCAQEEIEi9y6pSKu1kDZcIfQQAnojl1iFxm32W0DVCp2P6HRrkoAcGBSuBBAAK +oUQDQgAEoijIYF12bpA0tcjyQnWZGQ4lzdBGR+hK/5al/M+zFgFwvWHoWf6yJsSB +ymviB5yUaH+cE+/3LXlGbpRzYKLBYQ== +-----END EC PRIVATE KEY----- diff --git a/meerkat-common/src/test/resources/certs/root-ca/root-ca.crt b/meerkat-common/src/test/resources/certs/root-ca/root-ca.crt index e884528..d0bd1fa 100644 --- a/meerkat-common/src/test/resources/certs/root-ca/root-ca.crt +++ b/meerkat-common/src/test/resources/certs/root-ca/root-ca.crt @@ -1,17 +1,17 @@ ------BEGIN CERTIFICATE----- -MIICpTCCAkygAwIBAgIJAJoVb07aGgNaMAoGCCqGSM49BAMCMIGwMQswCQYDVQQG -EwJJTDETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwISGVyemxpeWExFDAS -BgNVBAoMC0lEQyBIZXJsaXlhMR8wHQYDVQQLDBZNZWVya2F0IFZvdGluZyBQcm9q -ZWN0MRgwFgYDVQQDDA9UZXN0aW5nIFJvb3QgQ0ExKDAmBgkqhkiG9w0BCQEWGXRl -c3RpbmctY2FAZmFjdGNlbnRlci5vcmcwHhcNMTUxMTExMTUzODE4WhcNMjUxMTA4 -MTUzODE4WjCBsDELMAkGA1UEBhMCSUwxEzARBgNVBAgMClNvbWUtU3RhdGUxETAP -BgNVBAcMCEhlcnpsaXlhMRQwEgYDVQQKDAtJREMgSGVybGl5YTEfMB0GA1UECwwW -TWVlcmthdCBWb3RpbmcgUHJvamVjdDEYMBYGA1UEAwwPVGVzdGluZyBSb290IENB -MSgwJgYJKoZIhvcNAQkBFhl0ZXN0aW5nLWNhQGZhY3RjZW50ZXIub3JnMFYwEAYH -KoZIzj0CAQYFK4EEAAoDQgAEoijIYF12bpA0tcjyQnWZGQ4lzdBGR+hK/5al/M+z -FgFwvWHoWf6yJsSBymviB5yUaH+cE+/3LXlGbpRzYKLBYaNQME4wHQYDVR0OBBYE -FIkP0vV8uZfgD0IGguxhefc4/LWiMB8GA1UdIwQYMBaAFIkP0vV8uZfgD0IGguxh -efc4/LWiMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDRwAwRAIgNftHrW30Git8 -VFQKyMCkasauSpEHpAGdcRAhRHqUQMUCIDxw++trz/Iv8818xVB1ARr9EQAmH0aC -7MHETGuiBC7L ------END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICpTCCAkygAwIBAgIJAJoVb07aGgNaMAoGCCqGSM49BAMCMIGwMQswCQYDVQQG +EwJJTDETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwISGVyemxpeWExFDAS +BgNVBAoMC0lEQyBIZXJsaXlhMR8wHQYDVQQLDBZNZWVya2F0IFZvdGluZyBQcm9q +ZWN0MRgwFgYDVQQDDA9UZXN0aW5nIFJvb3QgQ0ExKDAmBgkqhkiG9w0BCQEWGXRl +c3RpbmctY2FAZmFjdGNlbnRlci5vcmcwHhcNMTUxMTExMTUzODE4WhcNMjUxMTA4 +MTUzODE4WjCBsDELMAkGA1UEBhMCSUwxEzARBgNVBAgMClNvbWUtU3RhdGUxETAP +BgNVBAcMCEhlcnpsaXlhMRQwEgYDVQQKDAtJREMgSGVybGl5YTEfMB0GA1UECwwW +TWVlcmthdCBWb3RpbmcgUHJvamVjdDEYMBYGA1UEAwwPVGVzdGluZyBSb290IENB +MSgwJgYJKoZIhvcNAQkBFhl0ZXN0aW5nLWNhQGZhY3RjZW50ZXIub3JnMFYwEAYH +KoZIzj0CAQYFK4EEAAoDQgAEoijIYF12bpA0tcjyQnWZGQ4lzdBGR+hK/5al/M+z +FgFwvWHoWf6yJsSBymviB5yUaH+cE+/3LXlGbpRzYKLBYaNQME4wHQYDVR0OBBYE +FIkP0vV8uZfgD0IGguxhefc4/LWiMB8GA1UdIwQYMBaAFIkP0vV8uZfgD0IGguxh +efc4/LWiMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDRwAwRAIgNftHrW30Git8 +VFQKyMCkasauSpEHpAGdcRAhRHqUQMUCIDxw++trz/Iv8818xVB1ARr9EQAmH0aC +7MHETGuiBC7L +-----END CERTIFICATE----- diff --git a/meerkat-common/src/test/resources/certs/secp256k1.pem b/meerkat-common/src/test/resources/certs/secp256k1.pem index d707107..32d952e 100644 --- a/meerkat-common/src/test/resources/certs/secp256k1.pem +++ b/meerkat-common/src/test/resources/certs/secp256k1.pem @@ -1,3 +1,3 @@ ------BEGIN EC PARAMETERS----- -BgUrgQQACg== ------END EC PARAMETERS----- +-----BEGIN EC PARAMETERS----- +BgUrgQQACg== +-----END EC PARAMETERS----- diff --git a/polling-station/build.gradle b/polling-station/build.gradle index 4635668..68aad49 100644 --- a/polling-station/build.gradle +++ b/polling-station/build.gradle @@ -1,202 +1,202 @@ - -plugins { - id "us.kirchmeier.capsule" version "1.0.1" - id 'com.google.protobuf' version '0.7.0' -} - -apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'idea' - -apply plugin: 'maven-publish' - -// Uncomment the lines below to define an application -// (this will also allow you to build a "fatCapsule" which includes -// the entire application, including all dependencies in a single jar) -//apply plugin: 'application' -//mainClassName='your.main.ApplicationClass' - - -// Is this a snapshot version? -ext { isSnapshot = false } - -ext { - groupId = 'org.factcenter.meerkat' - nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" - - // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) - // Should be set in ${HOME}/.gradle/gradle.properties - nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" - nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" -} - -description = "Meerkat polling-station application" - -// Your project version -version = "0.0" - -version += "${isSnapshot ? '-SNAPSHOT' : ''}" - - -dependencies { - // Meerkat common - compile project(':meerkat-common') - compile project(':restful-api-common') - - // Jersey for RESTful API - compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.5.+' - - // Servlets - compile 'org.eclipse.jetty:jetty-server:9.3.+' - compile 'org.eclipse.jetty:jetty-servlet:9.3.+' - - // Logging - compile 'org.slf4j:slf4j-api:1.7.7' - runtime 'ch.qos.logback:logback-classic:1.1.2' - runtime 'ch.qos.logback:logback-core:1.1.2' - - // Google protobufs - compile 'com.google.protobuf:protobuf-java:3.+' - - testCompile 'junit:junit:4.+' - - runtime 'org.codehaus.groovy:groovy:2.4.+' -} - - -/*==== You probably don't have to edit below this line =======*/ - -protobuf { - // Configure the protoc executable - protoc { - // Download from repositories - artifact = 'com.google.protobuf:protoc:3.+' - } -} - - -idea { - module { - project.sourceSets.each { sourceSet -> - - def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" - - // add protobuf generated sources to generated source dir. - if ("test".equals(sourceSet.name)) { - testSourceDirs += file(srcDir) - } else { - sourceDirs += file(srcDir) - } - generatedSourceDirs += file(srcDir) - - } - - // Don't exclude build directory - excludeDirs -= file(buildDir) - } -} - -/*=================================== - * "Fat" Build targets - *===================================*/ - - -if (project.hasProperty('mainClassName') && (mainClassName != null)) { - - task mavenCapsule(type: MavenCapsule) { - description = "Generate a capsule jar that automatically downloads and caches dependencies when run." - applicationClass mainClassName - destinationDir = buildDir - } - - task fatCapsule(type: FatCapsule) { - description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class" - - destinationDir = buildDir - - def fatMain = hasProperty('fatmain') ? fatmain : mainClassName - - applicationClass fatMain - - def testJar = hasProperty('test') - - if (hasProperty('fatmain')) { - appendix = "fat-${fatMain}" - } else { - appendix = "fat" - } - - if (testJar) { - from sourceSets.test.output - } - } -} - -/*=================================== - * Repositories - *===================================*/ - -repositories { - - // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) - maven { - url nexusRepository - - if (isSnapshot) { - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } - - // Use local maven repository - mavenLocal() - - // Use 'maven central' for other dependencies. - mavenCentral() -} - -task "info" << { - println "Project: ${project.name}" -println "Description: ${project.description}" - println "--------------------------" - println "GroupId: $groupId" - println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" - println "" -} -info.description 'Print some information about project parameters' - - -/*=================================== - * Publishing - *===================================*/ - -publishing { - publications { - mavenJava(MavenPublication) { - groupId project.groupId - pom.withXml { - asNode().appendNode('description', project.description) - } - from project.components.java - - } - } - repositories { - maven { - url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } -} - - - + +plugins { + id "us.kirchmeier.capsule" version "1.0.1" + id 'com.google.protobuf' version '0.7.0' +} + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'idea' + +apply plugin: 'maven-publish' + +// Uncomment the lines below to define an application +// (this will also allow you to build a "fatCapsule" which includes +// the entire application, including all dependencies in a single jar) +//apply plugin: 'application' +//mainClassName='your.main.ApplicationClass' + + +// Is this a snapshot version? +ext { isSnapshot = false } + +ext { + groupId = 'org.factcenter.meerkat' + nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" + + // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) + // Should be set in ${HOME}/.gradle/gradle.properties + nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" + nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" +} + +description = "Meerkat polling-station application" + +// Your project version +version = "0.0" + +version += "${isSnapshot ? '-SNAPSHOT' : ''}" + + +dependencies { + // Meerkat common + compile project(':meerkat-common') + compile project(':restful-api-common') + + // Jersey for RESTful API + compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.5.+' + + // Servlets + compile 'org.eclipse.jetty:jetty-server:9.3.+' + compile 'org.eclipse.jetty:jetty-servlet:9.3.+' + + // Logging + compile 'org.slf4j:slf4j-api:1.7.7' + runtime 'ch.qos.logback:logback-classic:1.1.2' + runtime 'ch.qos.logback:logback-core:1.1.2' + + // Google protobufs + compile 'com.google.protobuf:protobuf-java:3.+' + + testCompile 'junit:junit:4.+' + + runtime 'org.codehaus.groovy:groovy:2.4.+' +} + + +/*==== You probably don't have to edit below this line =======*/ + +protobuf { + // Configure the protoc executable + protoc { + // Download from repositories + artifact = 'com.google.protobuf:protoc:3.+' + } +} + + +idea { + module { + project.sourceSets.each { sourceSet -> + + def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" + + // add protobuf generated sources to generated source dir. + if ("test".equals(sourceSet.name)) { + testSourceDirs += file(srcDir) + } else { + sourceDirs += file(srcDir) + } + generatedSourceDirs += file(srcDir) + + } + + // Don't exclude build directory + excludeDirs -= file(buildDir) + } +} + +/*=================================== + * "Fat" Build targets + *===================================*/ + + +if (project.hasProperty('mainClassName') && (mainClassName != null)) { + + task mavenCapsule(type: MavenCapsule) { + description = "Generate a capsule jar that automatically downloads and caches dependencies when run." + applicationClass mainClassName + destinationDir = buildDir + } + + task fatCapsule(type: FatCapsule) { + description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class" + + destinationDir = buildDir + + def fatMain = hasProperty('fatmain') ? fatmain : mainClassName + + applicationClass fatMain + + def testJar = hasProperty('test') + + if (hasProperty('fatmain')) { + appendix = "fat-${fatMain}" + } else { + appendix = "fat" + } + + if (testJar) { + from sourceSets.test.output + } + } +} + +/*=================================== + * Repositories + *===================================*/ + +repositories { + + // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) + maven { + url nexusRepository + + if (isSnapshot) { + credentials { username + password + + username nexusUser + password nexusPassword + } + } + } + + // Use local maven repository + mavenLocal() + + // Use 'maven central' for other dependencies. + mavenCentral() +} + +task "info" << { + println "Project: ${project.name}" +println "Description: ${project.description}" + println "--------------------------" + println "GroupId: $groupId" + println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" + println "" +} +info.description 'Print some information about project parameters' + + +/*=================================== + * Publishing + *===================================*/ + +publishing { + publications { + mavenJava(MavenPublication) { + groupId project.groupId + pom.withXml { + asNode().appendNode('description', project.description) + } + from project.components.java + + } + } + repositories { + maven { + url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + credentials { username + password + + username nexusUser + password nexusPassword + } + } + } +} + + + diff --git a/restful-api-common/.gitignore b/restful-api-common/.gitignore index 3e2fcc7..ae3c172 100644 --- a/restful-api-common/.gitignore +++ b/restful-api-common/.gitignore @@ -1 +1 @@ -/bin/ +/bin/ diff --git a/restful-api-common/build.gradle b/restful-api-common/build.gradle index 483790e..3c0ad1e 100644 --- a/restful-api-common/build.gradle +++ b/restful-api-common/build.gradle @@ -1,160 +1,160 @@ - -plugins { - id "us.kirchmeier.capsule" version "1.0.1" - id 'com.google.protobuf' version '0.7.0' -} - -apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'idea' - -// Uncomment both lines below to define an application (must set mainClassName -//apply plugin: 'application' -//mainClassName='your.main.ApplicationClass' - -apply plugin: 'maven-publish' - -// Is this a snapshot version? -ext { isSnapshot = false } - -ext { - groupId = 'org.factcenter.meerkat' - nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" - - // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) - // Should be set in ${HOME}/.gradle/gradle.properties - nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" - nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" -} - -description = "Common classes for implementing Meerkat's RESTful API" - -// Your project version -version = "0.0.1" - -version += "${isSnapshot ? '-SNAPSHOT' : ''}" - - -dependencies { - // Meerkat common - compile project(':meerkat-common') - - // Jersey for RESTful API - compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.22.+' - - // Logging - compile 'org.slf4j:slf4j-api:1.7.7' - runtime 'ch.qos.logback:logback-classic:1.1.2' - runtime 'ch.qos.logback:logback-core:1.1.2' - - // Google protobufs - compile 'com.google.protobuf:protobuf-java:3.+' - - testCompile 'junit:junit:4.+' - - runtime 'org.codehaus.groovy:groovy:2.4.+' -} - - -/*==== You probably don't have to edit below this line =======*/ - -protobuf { - // Configure the protoc executable - protoc { - // Download from repositories - artifact = 'com.google.protobuf:protoc:3.+' - } -} - - -idea { - module { - project.sourceSets.each { sourceSet -> - - def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" - - // add protobuf generated sources to generated source dir. - if ("test".equals(sourceSet.name)) { - testSourceDirs += file(srcDir) - } else { - sourceDirs += file(srcDir) - } - generatedSourceDirs += file(srcDir) - - } - - // Don't exclude build directory - excludeDirs -= file(buildDir) - } -} - - - -/*=================================== - * Repositories - *===================================*/ - -repositories { - - // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) - maven { - url nexusRepository - - if (isSnapshot) { - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } - - // Use local maven repository - mavenLocal() - - // Use 'maven central' for other dependencies. - mavenCentral() -} - -task "info" << { - println "Project: ${project.name}" -println "Description: ${project.description}" - println "--------------------------" - println "GroupId: $groupId" - println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" - println "" -} -info.description 'Print some information about project parameters' - - -/*=================================== - * Publishing - *===================================*/ - -publishing { - publications { - mavenJava(MavenPublication) { - groupId project.groupId - pom.withXml { - asNode().appendNode('description', project.description) - } - from project.components.java - - } - } - repositories { - maven { - url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } -} - - - + +plugins { + id "us.kirchmeier.capsule" version "1.0.1" + id 'com.google.protobuf' version '0.7.0' +} + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'idea' + +// Uncomment both lines below to define an application (must set mainClassName +//apply plugin: 'application' +//mainClassName='your.main.ApplicationClass' + +apply plugin: 'maven-publish' + +// Is this a snapshot version? +ext { isSnapshot = false } + +ext { + groupId = 'org.factcenter.meerkat' + nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" + + // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) + // Should be set in ${HOME}/.gradle/gradle.properties + nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" + nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" +} + +description = "Common classes for implementing Meerkat's RESTful API" + +// Your project version +version = "0.0.1" + +version += "${isSnapshot ? '-SNAPSHOT' : ''}" + + +dependencies { + // Meerkat common + compile project(':meerkat-common') + + // Jersey for RESTful API + compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.22.+' + + // Logging + compile 'org.slf4j:slf4j-api:1.7.7' + runtime 'ch.qos.logback:logback-classic:1.1.2' + runtime 'ch.qos.logback:logback-core:1.1.2' + + // Google protobufs + compile 'com.google.protobuf:protobuf-java:3.+' + + testCompile 'junit:junit:4.+' + + runtime 'org.codehaus.groovy:groovy:2.4.+' +} + + +/*==== You probably don't have to edit below this line =======*/ + +protobuf { + // Configure the protoc executable + protoc { + // Download from repositories + artifact = 'com.google.protobuf:protoc:3.+' + } +} + + +idea { + module { + project.sourceSets.each { sourceSet -> + + def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" + + // add protobuf generated sources to generated source dir. + if ("test".equals(sourceSet.name)) { + testSourceDirs += file(srcDir) + } else { + sourceDirs += file(srcDir) + } + generatedSourceDirs += file(srcDir) + + } + + // Don't exclude build directory + excludeDirs -= file(buildDir) + } +} + + + +/*=================================== + * Repositories + *===================================*/ + +repositories { + + // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) + maven { + url nexusRepository + + if (isSnapshot) { + credentials { username + password + + username nexusUser + password nexusPassword + } + } + } + + // Use local maven repository + mavenLocal() + + // Use 'maven central' for other dependencies. + mavenCentral() +} + +task "info" << { + println "Project: ${project.name}" +println "Description: ${project.description}" + println "--------------------------" + println "GroupId: $groupId" + println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" + println "" +} +info.description 'Print some information about project parameters' + + +/*=================================== + * Publishing + *===================================*/ + +publishing { + publications { + mavenJava(MavenPublication) { + groupId project.groupId + pom.withXml { + asNode().appendNode('description', project.description) + } + from project.components.java + + } + } + repositories { + maven { + url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + credentials { username + password + + username nexusUser + password nexusPassword + } + } + } +} + + + diff --git a/restful-api-common/src/main/java/meerkat/rest/Constants.java b/restful-api-common/src/main/java/meerkat/rest/Constants.java index e3e469a..2c04248 100644 --- a/restful-api-common/src/main/java/meerkat/rest/Constants.java +++ b/restful-api-common/src/main/java/meerkat/rest/Constants.java @@ -1,8 +1,8 @@ -package meerkat.rest; - -/** - * Created by talm on 10/11/15. - */ -public interface Constants { - public static final String MEDIATYPE_PROTOBUF = "application/x-protobuf"; -} +package meerkat.rest; + +/** + * Created by talm on 10/11/15. + */ +public interface Constants { + public static final String MEDIATYPE_PROTOBUF = "application/x-protobuf"; +} diff --git a/restful-api-common/src/main/java/meerkat/rest/ProtobufMessageBodyReader.java b/restful-api-common/src/main/java/meerkat/rest/ProtobufMessageBodyReader.java index a139e17..d09e213 100644 --- a/restful-api-common/src/main/java/meerkat/rest/ProtobufMessageBodyReader.java +++ b/restful-api-common/src/main/java/meerkat/rest/ProtobufMessageBodyReader.java @@ -1,41 +1,41 @@ -package meerkat.rest; - -import com.google.protobuf.GeneratedMessage; -import com.google.protobuf.Message; - - -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.ext.MessageBodyReader; -import javax.ws.rs.ext.Provider; -import java.io.IOException; -import java.io.InputStream; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Type; - -import static meerkat.rest.Constants.*; - -@Provider -@Consumes(MEDIATYPE_PROTOBUF) -public class ProtobufMessageBodyReader implements MessageBodyReader { - @Override - public boolean isReadable(Class type, Type genericType, Annotation[] annotations, - MediaType mediaType) { - return Message.class.isAssignableFrom(type); - } - - @Override - public Message readFrom(Class type, Type genericType, Annotation[] annotations, - MediaType mediaType, MultivaluedMap httpHeaders, - InputStream entityStream) throws IOException, WebApplicationException { - try { - Method newBuilder = type.getMethod("newBuilder"); - GeneratedMessage.Builder builder = (GeneratedMessage.Builder) newBuilder.invoke(type); - return builder.mergeFrom(entityStream).build(); - } catch (Exception e) { - throw new WebApplicationException(e); - } - } -} +package meerkat.rest; + +import com.google.protobuf.GeneratedMessage; +import com.google.protobuf.Message; + + +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.Provider; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Type; + +import static meerkat.rest.Constants.*; + +@Provider +@Consumes(MEDIATYPE_PROTOBUF) +public class ProtobufMessageBodyReader implements MessageBodyReader { + @Override + public boolean isReadable(Class type, Type genericType, Annotation[] annotations, + MediaType mediaType) { + return Message.class.isAssignableFrom(type); + } + + @Override + public Message readFrom(Class type, Type genericType, Annotation[] annotations, + MediaType mediaType, MultivaluedMap httpHeaders, + InputStream entityStream) throws IOException, WebApplicationException { + try { + Method newBuilder = type.getMethod("newBuilder"); + GeneratedMessage.Builder builder = (GeneratedMessage.Builder) newBuilder.invoke(type); + return builder.mergeFrom(entityStream).build(); + } catch (Exception e) { + throw new WebApplicationException(e); + } + } +} diff --git a/restful-api-common/src/main/java/meerkat/rest/ProtobufMessageBodyWriter.java b/restful-api-common/src/main/java/meerkat/rest/ProtobufMessageBodyWriter.java index 2397b4b..b8ea503 100644 --- a/restful-api-common/src/main/java/meerkat/rest/ProtobufMessageBodyWriter.java +++ b/restful-api-common/src/main/java/meerkat/rest/ProtobufMessageBodyWriter.java @@ -1,41 +1,41 @@ -package meerkat.rest; - -import com.google.protobuf.Message; - -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.ext.MessageBodyWriter; -import javax.ws.rs.ext.Provider; - -import java.io.IOException; -import java.io.OutputStream; -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; - -import static meerkat.rest.Constants.*; - -@Provider -@Produces(MEDIATYPE_PROTOBUF) -public class ProtobufMessageBodyWriter implements MessageBodyWriter { - @Override - public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, - MediaType mediaType) { - return Message.class.isAssignableFrom(type); - } - - @Override - public long getSize(Message message, Class type, Type genericType, Annotation[] annotations, - MediaType mediaType) { - return -1; - } - - @Override - public void writeTo(Message message, Class type, Type genericType, Annotation[] annotations, - MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) - throws IOException, WebApplicationException { - message.writeTo(entityStream); - } -} - - +package meerkat.rest; + +import com.google.protobuf.Message; + +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import static meerkat.rest.Constants.*; + +@Provider +@Produces(MEDIATYPE_PROTOBUF) +public class ProtobufMessageBodyWriter implements MessageBodyWriter { + @Override + public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, + MediaType mediaType) { + return Message.class.isAssignableFrom(type); + } + + @Override + public long getSize(Message message, Class type, Type genericType, Annotation[] annotations, + MediaType mediaType) { + return -1; + } + + @Override + public void writeTo(Message message, Class type, Type genericType, Annotation[] annotations, + MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) + throws IOException, WebApplicationException { + message.writeTo(entityStream); + } +} + + diff --git a/settings.gradle b/settings.gradle index 236a0d1..851bfc3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,8 +1,8 @@ -include 'meerkat-common' -include 'voting-booth' -include 'bulletin-board-server' -include 'polling-station' -include 'restful-api-common' -include 'bulletin-board-client' -include 'distributed-key-generation' - +include 'meerkat-common' +include 'voting-booth' +include 'bulletin-board-server' +include 'polling-station' +include 'restful-api-common' +include 'bulletin-board-client' +include 'distributed-key-generation' + diff --git a/voting-booth/build.gradle b/voting-booth/build.gradle index 7619b48..8931471 100644 --- a/voting-booth/build.gradle +++ b/voting-booth/build.gradle @@ -1,4 +1,3 @@ - plugins { id "us.kirchmeier.capsule" version "1.0.1" id 'com.google.protobuf' version '0.7.0' @@ -188,4 +187,3 @@ publishing { } } } -