Merge branch 'master' into vbdev2
Conflicts: meerkat-common/src/main/proto/meerkat/voting.protovbdev2
commit
49551dc36b
|
@ -1,16 +1,18 @@
|
||||||
.gradle
|
.gradle
|
||||||
.idea
|
.idea
|
||||||
build
|
build
|
||||||
bin
|
bin
|
||||||
.settings
|
.settings
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
out
|
out
|
||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
**/*.swp
|
**/*.swp
|
||||||
*.prefs
|
*.prefs
|
||||||
*.project
|
*.project
|
||||||
*.classpath
|
*.classpath
|
||||||
bulletin-board-server/local-instances/meerkat.db
|
*.db
|
||||||
|
*.sql
|
||||||
|
.arcconfig
|
||||||
|
|
20
build.gradle
20
build.gradle
|
@ -1,10 +1,10 @@
|
||||||
|
|
||||||
subprojects { proj ->
|
subprojects { proj ->
|
||||||
proj.afterEvaluate {
|
proj.afterEvaluate {
|
||||||
// Used to generate initial maven-dir layout
|
// Used to generate initial maven-dir layout
|
||||||
task "create-dirs" { description = "Create default maven directory structure" } << {
|
task "create-dirs" { description = "Create default maven directory structure" } << {
|
||||||
sourceSets*.java.srcDirs*.each { it.mkdirs() }
|
sourceSets*.java.srcDirs*.each { it.mkdirs() }
|
||||||
sourceSets*.resources.srcDirs*.each { it.mkdirs() }
|
sourceSets*.resources.srcDirs*.each { it.mkdirs() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,220 +1,220 @@
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "us.kirchmeier.capsule" version "1.0.1"
|
id "us.kirchmeier.capsule" version "1.0.1"
|
||||||
id 'com.google.protobuf' version '0.7.0'
|
id 'com.google.protobuf' version '0.7.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'eclipse'
|
apply plugin: 'eclipse'
|
||||||
apply plugin: 'idea'
|
apply plugin: 'idea'
|
||||||
|
|
||||||
apply plugin: 'maven-publish'
|
apply plugin: 'maven-publish'
|
||||||
|
|
||||||
// Uncomment the lines below to define an application
|
// Uncomment the lines below to define an application
|
||||||
// (this will also allow you to build a "fatCapsule" which includes
|
// (this will also allow you to build a "fatCapsule" which includes
|
||||||
// the entire application, including all dependencies in a single jar)
|
// the entire application, including all dependencies in a single jar)
|
||||||
//apply plugin: 'application'
|
//apply plugin: 'application'
|
||||||
//mainClassName='your.main.ApplicationClass'
|
//mainClassName='your.main.ApplicationClass'
|
||||||
|
|
||||||
// Is this a snapshot version?
|
// Is this a snapshot version?
|
||||||
ext { isSnapshot = false }
|
ext { isSnapshot = false }
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
groupId = 'org.factcenter.meerkat'
|
groupId = 'org.factcenter.meerkat'
|
||||||
nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/"
|
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)
|
// Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing)
|
||||||
// Should be set in ${HOME}/.gradle/gradle.properties
|
// Should be set in ${HOME}/.gradle/gradle.properties
|
||||||
nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : ""
|
nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : ""
|
||||||
nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : ""
|
nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
description = "TODO: Add a description"
|
description = "TODO: Add a description"
|
||||||
|
|
||||||
// Your project version
|
// Your project version
|
||||||
version = "0.0"
|
version = "0.0"
|
||||||
|
|
||||||
version += "${isSnapshot ? '-SNAPSHOT' : ''}"
|
version += "${isSnapshot ? '-SNAPSHOT' : ''}"
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Meerkat common
|
// Meerkat common
|
||||||
compile project(':meerkat-common')
|
compile project(':meerkat-common')
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
compile 'org.slf4j:slf4j-api:1.7.7'
|
compile 'org.slf4j:slf4j-api:1.7.7'
|
||||||
runtime 'ch.qos.logback:logback-classic:1.1.2'
|
runtime 'ch.qos.logback:logback-classic:1.1.2'
|
||||||
runtime 'ch.qos.logback:logback-core:1.1.2'
|
runtime 'ch.qos.logback:logback-core:1.1.2'
|
||||||
|
|
||||||
// Google protobufs
|
// Google protobufs
|
||||||
compile 'com.google.protobuf:protobuf-java:3.+'
|
compile 'com.google.protobuf:protobuf-java:3.+'
|
||||||
|
|
||||||
testCompile 'junit:junit:4.+'
|
testCompile 'junit:junit:4.+'
|
||||||
|
|
||||||
runtime 'org.codehaus.groovy:groovy:2.4.+'
|
runtime 'org.codehaus.groovy:groovy:2.4.+'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*==== You probably don't have to edit below this line =======*/
|
/*==== You probably don't have to edit below this line =======*/
|
||||||
|
|
||||||
// Setup test configuration that can appear as a dependency in
|
// Setup test configuration that can appear as a dependency in
|
||||||
// other subprojects
|
// other subprojects
|
||||||
configurations {
|
configurations {
|
||||||
testOutput.extendsFrom (testCompile)
|
testOutput.extendsFrom (testCompile)
|
||||||
}
|
}
|
||||||
|
|
||||||
task testJar(type: Jar, dependsOn: testClasses) {
|
task testJar(type: Jar, dependsOn: testClasses) {
|
||||||
classifier = 'tests'
|
classifier = 'tests'
|
||||||
from sourceSets.test.output
|
from sourceSets.test.output
|
||||||
}
|
}
|
||||||
|
|
||||||
artifacts {
|
artifacts {
|
||||||
testOutput testJar
|
testOutput testJar
|
||||||
}
|
}
|
||||||
|
|
||||||
// The run task added by the application plugin
|
// The run task added by the application plugin
|
||||||
// is also of type JavaExec.
|
// is also of type JavaExec.
|
||||||
tasks.withType(JavaExec) {
|
tasks.withType(JavaExec) {
|
||||||
// Assign all Java system properties from
|
// Assign all Java system properties from
|
||||||
// the command line to the JavaExec task.
|
// the command line to the JavaExec task.
|
||||||
systemProperties System.properties
|
systemProperties System.properties
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protobuf {
|
protobuf {
|
||||||
// Configure the protoc executable
|
// Configure the protoc executable
|
||||||
protoc {
|
protoc {
|
||||||
// Download from repositories
|
// Download from repositories
|
||||||
artifact = 'com.google.protobuf:protoc:3.+'
|
artifact = 'com.google.protobuf:protoc:3.+'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
idea {
|
idea {
|
||||||
module {
|
module {
|
||||||
project.sourceSets.each { sourceSet ->
|
project.sourceSets.each { sourceSet ->
|
||||||
|
|
||||||
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
||||||
|
|
||||||
println "Adding $srcDir"
|
println "Adding $srcDir"
|
||||||
// add protobuf generated sources to generated source dir.
|
// add protobuf generated sources to generated source dir.
|
||||||
if ("test".equals(sourceSet.name)) {
|
if ("test".equals(sourceSet.name)) {
|
||||||
testSourceDirs += file(srcDir)
|
testSourceDirs += file(srcDir)
|
||||||
} else {
|
} else {
|
||||||
sourceDirs += file(srcDir)
|
sourceDirs += file(srcDir)
|
||||||
}
|
}
|
||||||
generatedSourceDirs += file(srcDir)
|
generatedSourceDirs += file(srcDir)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't exclude build directory
|
// Don't exclude build directory
|
||||||
excludeDirs -= file(buildDir)
|
excludeDirs -= file(buildDir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*===================================
|
/*===================================
|
||||||
* "Fat" Build targets
|
* "Fat" Build targets
|
||||||
*===================================*/
|
*===================================*/
|
||||||
|
|
||||||
|
|
||||||
if (project.hasProperty('mainClassName') && (mainClassName != null)) {
|
if (project.hasProperty('mainClassName') && (mainClassName != null)) {
|
||||||
|
|
||||||
task mavenCapsule(type: MavenCapsule) {
|
task mavenCapsule(type: MavenCapsule) {
|
||||||
description = "Generate a capsule jar that automatically downloads and caches dependencies when run."
|
description = "Generate a capsule jar that automatically downloads and caches dependencies when run."
|
||||||
applicationClass mainClassName
|
applicationClass mainClassName
|
||||||
destinationDir = buildDir
|
destinationDir = buildDir
|
||||||
}
|
}
|
||||||
|
|
||||||
task fatCapsule(type: FatCapsule) {
|
task fatCapsule(type: FatCapsule) {
|
||||||
description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class"
|
description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class"
|
||||||
|
|
||||||
destinationDir = buildDir
|
destinationDir = buildDir
|
||||||
|
|
||||||
def fatMain = hasProperty('fatmain') ? fatmain : mainClassName
|
def fatMain = hasProperty('fatmain') ? fatmain : mainClassName
|
||||||
|
|
||||||
applicationClass fatMain
|
applicationClass fatMain
|
||||||
|
|
||||||
def testJar = hasProperty('test')
|
def testJar = hasProperty('test')
|
||||||
|
|
||||||
if (hasProperty('fatmain')) {
|
if (hasProperty('fatmain')) {
|
||||||
appendix = "fat-${fatMain}"
|
appendix = "fat-${fatMain}"
|
||||||
} else {
|
} else {
|
||||||
appendix = "fat"
|
appendix = "fat"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (testJar) {
|
if (testJar) {
|
||||||
from sourceSets.test.output
|
from sourceSets.test.output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*===================================
|
/*===================================
|
||||||
* Repositories
|
* Repositories
|
||||||
*===================================*/
|
*===================================*/
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
|
||||||
// Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral)
|
// Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral)
|
||||||
maven {
|
maven {
|
||||||
url nexusRepository
|
url nexusRepository
|
||||||
|
|
||||||
if (isSnapshot) {
|
if (isSnapshot) {
|
||||||
credentials { username
|
credentials { username
|
||||||
password
|
password
|
||||||
|
|
||||||
username nexusUser
|
username nexusUser
|
||||||
password nexusPassword
|
password nexusPassword
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use local maven repository
|
// Use local maven repository
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
|
|
||||||
// Use 'maven central' for other dependencies.
|
// Use 'maven central' for other dependencies.
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
task "info" << {
|
task "info" << {
|
||||||
println "Project: ${project.name}"
|
println "Project: ${project.name}"
|
||||||
println "Description: ${project.description}"
|
println "Description: ${project.description}"
|
||||||
println "--------------------------"
|
println "--------------------------"
|
||||||
println "GroupId: $groupId"
|
println "GroupId: $groupId"
|
||||||
println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})"
|
println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})"
|
||||||
println ""
|
println ""
|
||||||
}
|
}
|
||||||
info.description 'Print some information about project parameters'
|
info.description 'Print some information about project parameters'
|
||||||
|
|
||||||
|
|
||||||
/*===================================
|
/*===================================
|
||||||
* Publishing
|
* Publishing
|
||||||
*===================================*/
|
*===================================*/
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
publications {
|
publications {
|
||||||
mavenJava(MavenPublication) {
|
mavenJava(MavenPublication) {
|
||||||
groupId project.groupId
|
groupId project.groupId
|
||||||
pom.withXml {
|
pom.withXml {
|
||||||
asNode().appendNode('description', project.description)
|
asNode().appendNode('description', project.description)
|
||||||
}
|
}
|
||||||
from project.components.java
|
from project.components.java
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}"
|
url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}"
|
||||||
credentials { username
|
credentials { username
|
||||||
password
|
password
|
||||||
|
|
||||||
username nexusUser
|
username nexusUser
|
||||||
password nexusPassword
|
password nexusPassword
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ dependencies {
|
||||||
|
|
||||||
test {
|
test {
|
||||||
exclude '**/*IntegrationTest*'
|
exclude '**/*IntegrationTest*'
|
||||||
outputs.upToDateWhen { false }
|
// outputs.upToDateWhen { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
task integrationTest(type: Test) {
|
task integrationTest(type: Test) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import meerkat.protobuf.BulletinBoardAPI.BatchChunk;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.BatchData;
|
import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -11,15 +11,13 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class BatchDataContainer {
|
public class BatchDataContainer {
|
||||||
|
|
||||||
public final byte[] signerId;
|
public final MultiServerBatchIdentifier batchId;
|
||||||
public final int batchId;
|
public final List<BatchChunk> batchChunkList;
|
||||||
public final List<BatchData> batchDataList;
|
|
||||||
public final int startPosition;
|
public final int startPosition;
|
||||||
|
|
||||||
public BatchDataContainer(byte[] signerId, int batchId, List<BatchData> batchDataList, int startPosition) {
|
public BatchDataContainer(MultiServerBatchIdentifier batchId, List<BatchChunk> batchChunkList, int startPosition) {
|
||||||
this.signerId = signerId;
|
|
||||||
this.batchId = batchId;
|
this.batchId = batchId;
|
||||||
this.batchDataList = batchDataList;
|
this.batchChunkList = batchChunkList;
|
||||||
this.startPosition = startPosition;
|
this.startPosition = startPosition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,38 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||||
*
|
*
|
||||||
* This class handles bulletin client work.
|
* This class handles bulletin client work.
|
||||||
* It is meant to be used in a multi-threaded environment.
|
* It is meant to be used in a multi-threaded environment.
|
||||||
*/
|
*/
|
||||||
public abstract class BulletinClientWorker<IN> {
|
public abstract class BulletinClientWorker<IN> {
|
||||||
|
|
||||||
protected final IN payload; // Payload of the job
|
protected final IN payload; // Payload of the job
|
||||||
|
|
||||||
private int maxRetry; // Number of retries for this job; set to -1 for infinite retries
|
private int maxRetry; // Number of retries for this job; set to -1 for infinite retries
|
||||||
|
|
||||||
public BulletinClientWorker(IN payload, int maxRetry) {
|
public BulletinClientWorker(IN payload, int maxRetry) {
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
this.maxRetry = maxRetry;
|
this.maxRetry = maxRetry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IN getPayload() {
|
public IN getPayload() {
|
||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxRetry() {
|
public int getMaxRetry() {
|
||||||
return maxRetry;
|
return maxRetry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decMaxRetry(){
|
public void decMaxRetry(){
|
||||||
if (maxRetry > 0) {
|
if (maxRetry > 0) {
|
||||||
maxRetry--;
|
maxRetry--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRetry(){
|
public boolean isRetry(){
|
||||||
return (maxRetry != 0);
|
return (maxRetry != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,168 +1,490 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
|
import com.google.protobuf.Timestamp;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.protobuf.BulletinBoardAPI;
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
import meerkat.protobuf.Crypto.Signature;
|
||||||
import meerkat.protobuf.Voting.*;
|
import meerkat.protobuf.Voting.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 03-Mar-16.
|
* Created by Arbel Deutsch Peled on 03-Mar-16.
|
||||||
* This is a full-fledged implementation of a Bulletin Board Client
|
* 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
|
* 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
|
* 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
|
* The database also employs a synchronizer which makes sure local data is sent to the remote servers
|
||||||
*/
|
*/
|
||||||
public class CachedBulletinBoardClient implements SubscriptionAsyncBulletinBoardClient {
|
public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClient {
|
||||||
|
|
||||||
private final BulletinBoardClient localClient;
|
private final AsyncBulletinBoardClient localClient;
|
||||||
private AsyncBulletinBoardClient remoteClient;
|
private final AsyncBulletinBoardClient remoteClient;
|
||||||
private BulletinBoardSubscriber subscriber;
|
private final AsyncBulletinBoardClient queueClient;
|
||||||
|
private final BulletinBoardSubscriber subscriber;
|
||||||
|
private final BulletinBoardSynchronizer synchronizer;
|
||||||
|
|
||||||
private final int threadPoolSize;
|
private Thread syncThread;
|
||||||
private final long failDelayInMilliseconds;
|
|
||||||
private final long subscriptionIntervalInMilliseconds;
|
|
||||||
|
|
||||||
public CachedBulletinBoardClient(BulletinBoardClient localClient,
|
private final static int DEFAULT_WAIT_CAP = 3000;
|
||||||
int threadPoolSize,
|
private final static int DEFAULT_SLEEP_INTERVAL = 3000;
|
||||||
long failDelayInMilliseconds,
|
|
||||||
long subscriptionIntervalInMilliseconds)
|
private class SubscriptionStoreCallback implements FutureCallback<List<BulletinBoardMessage>> {
|
||||||
throws IllegalAccessException, InstantiationException {
|
|
||||||
|
private final FutureCallback<List<BulletinBoardMessage>> callback;
|
||||||
|
|
||||||
|
public SubscriptionStoreCallback(){
|
||||||
|
callback = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubscriptionStoreCallback(FutureCallback<List<BulletinBoardMessage>> callback){
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<BulletinBoardMessage> 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.localClient = localClient;
|
||||||
this.threadPoolSize = threadPoolSize;
|
this.remoteClient = remoteClient;
|
||||||
this.failDelayInMilliseconds = failDelayInMilliseconds;
|
this.subscriber = subscriber;
|
||||||
this.subscriptionIntervalInMilliseconds = subscriptionIntervalInMilliseconds;
|
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<Boolean> callback) {
|
||||||
|
|
||||||
|
return localClient.postMessage(msg, new FutureCallback<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Boolean result) {
|
||||||
|
remoteClient.postMessage(msg, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable t) {
|
||||||
|
if (callback != null)
|
||||||
|
callback.onFailure(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageID postMessage(BulletinBoardMessage msg, FutureCallback<Boolean> callback) {
|
public MessageID postAsBatch(final BulletinBoardMessage msg, final int chunkSize, final FutureCallback<Boolean> callback) {
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
return localClient.postAsBatch(msg, chunkSize, new FutureCallback<Boolean>() {
|
||||||
public MessageID postBatch(CompleteBatch completeBatch, FutureCallback<Boolean> callback) {
|
@Override
|
||||||
return null;
|
public void onSuccess(Boolean result) {
|
||||||
}
|
remoteClient.postAsBatch(msg, chunkSize, callback);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback<Boolean> callback) {
|
public void onFailure(Throwable t) {
|
||||||
|
if (callback != null)
|
||||||
|
callback.onFailure(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, int startPosition, FutureCallback<Boolean> callback) {
|
public void beginBatch(final Iterable<String> tags, final FutureCallback<BatchIdentifier> callback) {
|
||||||
|
|
||||||
|
localClient.beginBatch(tags, new FutureCallback<BatchIdentifier>() {
|
||||||
|
|
||||||
|
private BatchIdentifier localIdentifier;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(BatchIdentifier result) {
|
||||||
|
|
||||||
|
localIdentifier = result;
|
||||||
|
|
||||||
|
remoteClient.beginBatch(tags, new FutureCallback<BatchIdentifier>() {
|
||||||
|
@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
|
@Override
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
public void postBatchData(final BatchIdentifier batchIdentifier, final List<BatchChunk> batchChunkList,
|
||||||
|
final int startPosition, final FutureCallback<Boolean> 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<Boolean>() {
|
||||||
|
@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
|
@Override
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, int startPosition, FutureCallback<Boolean> callback) {
|
public void postBatchData(final BatchIdentifier batchIdentifier, final List<BatchChunk> batchChunkList, final FutureCallback<Boolean> 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<Boolean>() {
|
||||||
|
@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
|
@Override
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
public void closeBatch(final BatchIdentifier batchIdentifier, final Timestamp timestamp, final Iterable<Signature> signatures,
|
||||||
|
final FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
}
|
if (!(batchIdentifier instanceof CachedClientBatchIdentifier)){
|
||||||
|
throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class.");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
final CachedClientBatchIdentifier identifier = (CachedClientBatchIdentifier) batchIdentifier;
|
||||||
public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback<Boolean> callback) {
|
|
||||||
|
localClient.closeBatch(identifier.getLocalIdentifier(), timestamp, signatures, new FutureCallback<Boolean>() {
|
||||||
|
@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
|
@Override
|
||||||
public void getRedundancy(MessageID id, FutureCallback<Float> callback) {
|
public void getRedundancy(MessageID id, FutureCallback<Float> callback) {
|
||||||
|
|
||||||
}
|
remoteClient.getRedundancy(id, callback);
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback<CompleteBatch> callback) {
|
public void readMessages(MessageFilterList filterList, final FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||||
|
|
||||||
|
localClient.readMessages(filterList, callback);
|
||||||
|
|
||||||
|
subscriber.subscribe(filterList, new SubscriptionStoreCallback(callback));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readMessage(final MessageID msgID, final FutureCallback<BulletinBoardMessage> callback) {
|
||||||
|
|
||||||
|
localClient.readMessage(msgID, new FutureCallback<BulletinBoardMessage>() {
|
||||||
|
|
||||||
|
@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<BulletinBoardMessage>() {
|
||||||
|
|
||||||
|
@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<BulletinBoardMessage> callback) throws IllegalArgumentException {
|
||||||
|
|
||||||
|
localClient.readBatchData(stub, new FutureCallback<BulletinBoardMessage>() {
|
||||||
|
|
||||||
|
@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<BulletinBoardMessage>() {
|
||||||
|
|
||||||
|
@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
|
@Override
|
||||||
public void querySync(SyncQuery syncQuery, FutureCallback<SyncQueryResponse> callback) {
|
public void querySync(SyncQuery syncQuery, FutureCallback<SyncQueryResponse> callback) {
|
||||||
|
|
||||||
|
localClient.querySync(syncQuery, callback);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(BulletinBoardClientParams clientParams) {
|
/**
|
||||||
|
* This is a stub method
|
||||||
remoteClient.init(clientParams);
|
* All resources are assumed to be initialized
|
||||||
|
*/
|
||||||
ListeningScheduledExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(threadPoolSize));
|
public void init(BulletinBoardClientParams clientParams) {}
|
||||||
|
|
||||||
List<SubscriptionAsyncBulletinBoardClient> 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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||||
return null;
|
return localClient.postMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getRedundancy(MessageID id) {
|
public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException {
|
||||||
return 0;
|
MessageID result = localClient.postAsBatch(msg, chunkSize);
|
||||||
|
remoteClient.postAsBatch(msg, chunkSize);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) {
|
public float getRedundancy(MessageID id) throws CommunicationException {
|
||||||
return null;
|
return remoteClient.getRedundancy(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SyncQuery generateSyncQuery(GenerateSyncQueryParams GenerateSyncQueryParams) throws CommunicationException {
|
public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) throws CommunicationException {
|
||||||
return null;
|
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
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
localClient.close();
|
||||||
|
remoteClient.close();
|
||||||
|
synchronizer.stop();
|
||||||
|
try {
|
||||||
|
syncThread.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
//TODO: log interruption
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void subscribe(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
public void subscribe(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||||
|
subscriber.subscribe(filterList, new SubscriptionStoreCallback(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void subscribe(MessageFilterList filterList, long startEntry, FutureCallback<List<BulletinBoardMessage>> callback) {
|
public void subscribe(MessageFilterList filterList, long startEntry, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||||
|
subscriber.subscribe(filterList, startEntry, new SubscriptionStoreCallback(callback));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,20 +1,21 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.*;
|
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.CommunicationException;
|
||||||
import meerkat.comm.MessageInputStream;
|
import meerkat.comm.MessageInputStream;
|
||||||
import meerkat.comm.MessageInputStream.MessageInputStreamFactory;
|
import meerkat.comm.MessageInputStream.MessageInputStreamFactory;
|
||||||
import meerkat.comm.MessageOutputStream;
|
import meerkat.comm.MessageOutputStream;
|
||||||
import meerkat.crypto.concrete.SHA256Digest;
|
import meerkat.crypto.concrete.SHA256Digest;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
import meerkat.protobuf.Crypto.Signature;
|
||||||
import meerkat.protobuf.Voting.*;
|
import meerkat.protobuf.Voting.*;
|
||||||
import meerkat.util.BulletinBoardUtils;
|
import meerkat.util.BulletinBoardUtils;
|
||||||
|
|
||||||
import javax.ws.rs.NotFoundException;
|
import javax.ws.rs.NotFoundException;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
@ -22,28 +23,27 @@ import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 15-Mar-16.
|
* Created by Arbel Deutsch Peled on 15-Mar-16.
|
||||||
* This client is to be used mainly for testing.
|
* This client wraps a BulletinBoardServer in an asynchronous client.
|
||||||
* It 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.
|
* 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.
|
* 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 ListeningScheduledExecutorService executorService;
|
||||||
private final BatchDigest digest;
|
private final BulletinBoardDigest digest;
|
||||||
private final int subsrciptionDelay;
|
private final long subsrciptionDelay;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes an instance of the client
|
* Initializes an instance of the client
|
||||||
* @param server an initialized Bulletin Board Server instance which will perform the actual processing of the requests
|
* @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 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.server = server;
|
||||||
this.executorService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(threadNum));
|
this.executorService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(threadNum));
|
||||||
this.digest = new GenericBatchDigest(new SHA256Digest());
|
this.digest = new GenericBulletinBoardDigest(new SHA256Digest());
|
||||||
this.subsrciptionDelay = subscriptionDelay;
|
this.subsrciptionDelay = subscriptionDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean call() throws Exception {
|
public Boolean call() throws CommunicationException {
|
||||||
return server.postMessage(msg).getValue();
|
return server.postMessage(msg).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,51 +75,57 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC
|
||||||
|
|
||||||
private class CompleteBatchPoster implements Callable<Boolean> {
|
private class CompleteBatchPoster implements Callable<Boolean> {
|
||||||
|
|
||||||
private final CompleteBatch completeBatch;
|
private final BulletinBoardMessage msg;
|
||||||
|
private final int chunkSize;
|
||||||
|
|
||||||
public CompleteBatchPoster(CompleteBatch completeBatch) {
|
public CompleteBatchPoster(BulletinBoardMessage msg, int chunkSize) {
|
||||||
this.completeBatch = completeBatch;
|
this.msg = msg;
|
||||||
|
this.chunkSize = chunkSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean call() throws Exception {
|
public Boolean call() throws CommunicationException {
|
||||||
|
|
||||||
if (!server.beginBatch(completeBatch.getBeginBatchMessage()).getValue())
|
BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder()
|
||||||
return false;
|
.addAllTag(msg.getMsg().getTagList())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Int64Value batchId = server.beginBatch(beginBatchMessage);
|
||||||
|
|
||||||
|
BatchMessage.Builder builder = BatchMessage.newBuilder()
|
||||||
|
.setBatchId(batchId.getValue());
|
||||||
|
|
||||||
|
List<BatchChunk> batchChunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize);
|
||||||
|
|
||||||
int i=0;
|
int i=0;
|
||||||
for (BatchData data : completeBatch.getBatchDataList()){
|
for (BatchChunk chunk : batchChunkList){
|
||||||
|
|
||||||
BatchMessage message = BatchMessage.newBuilder()
|
server.postBatchMessage(builder.setSerialNum(i).setData(chunk).build());
|
||||||
.setSignerId(completeBatch.getSignature().getSignerId())
|
|
||||||
.setBatchId(completeBatch.getBeginBatchMessage().getBatchId())
|
|
||||||
.setSerialNum(i)
|
|
||||||
.setData(data)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
if (!server.postBatchMessage(message).getValue())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return server.closeBatchMessage(completeBatch.getCloseBatchMessage()).getValue();
|
CloseBatchMessage closeBatchMessage = BulletinBoardUtils.generateCloseBatchMessage(batchId, batchChunkList.size(), msg);
|
||||||
|
|
||||||
|
return server.closeBatch(closeBatchMessage).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageID postBatch(CompleteBatch completeBatch, FutureCallback<Boolean> callback) {
|
public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize, FutureCallback<Boolean> 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();
|
return digest.digestAsMessageID();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BatchBeginner implements Callable<Boolean> {
|
private class BatchBeginner implements Callable<SingleServerBatchIdentifier> {
|
||||||
|
|
||||||
private final BeginBatchMessage msg;
|
private final BeginBatchMessage msg;
|
||||||
|
|
||||||
|
@ -129,28 +135,31 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean call() throws Exception {
|
public SingleServerBatchIdentifier call() throws Exception {
|
||||||
return server.beginBatch(msg).getValue();
|
return new SingleServerBatchIdentifier(server.beginBatch(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback<Boolean> callback) {
|
public void beginBatch(Iterable<String> tags, FutureCallback<BatchIdentifier> callback) {
|
||||||
|
|
||||||
|
BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder()
|
||||||
|
.addAllTag(tags)
|
||||||
|
.build();
|
||||||
|
|
||||||
Futures.addCallback(executorService.submit(new BatchBeginner(beginBatchMessage)), callback);
|
Futures.addCallback(executorService.submit(new BatchBeginner(beginBatchMessage)), callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BatchDataPoster implements Callable<Boolean> {
|
private class BatchDataPoster implements Callable<Boolean> {
|
||||||
|
|
||||||
private final ByteString signerId;
|
private final SingleServerBatchIdentifier batchId;
|
||||||
private final int batchId;
|
private final List<BatchChunk> batchChunkList;
|
||||||
private final List<BatchData> batchDataList;
|
|
||||||
private final int startPosition;
|
private final int startPosition;
|
||||||
|
|
||||||
public BatchDataPoster(ByteString signerId, int batchId, List<BatchData> batchDataList, int startPosition) {
|
public BatchDataPoster(SingleServerBatchIdentifier batchId, List<BatchChunk> batchChunkList, int startPosition) {
|
||||||
this.signerId = signerId;
|
|
||||||
this.batchId = batchId;
|
this.batchId = batchId;
|
||||||
this.batchDataList = batchDataList;
|
this.batchChunkList = batchChunkList;
|
||||||
this.startPosition = startPosition;
|
this.startPosition = startPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,11 +168,10 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC
|
||||||
public Boolean call() throws Exception {
|
public Boolean call() throws Exception {
|
||||||
|
|
||||||
BatchMessage.Builder msgBuilder = BatchMessage.newBuilder()
|
BatchMessage.Builder msgBuilder = BatchMessage.newBuilder()
|
||||||
.setSignerId(signerId)
|
.setBatchId(batchId.getBatchId().getValue());
|
||||||
.setBatchId(batchId);
|
|
||||||
|
|
||||||
int i = startPosition;
|
int i = startPosition;
|
||||||
for (BatchData data : batchDataList){
|
for (BatchChunk data : batchChunkList){
|
||||||
|
|
||||||
msgBuilder.setSerialNum(i)
|
msgBuilder.setSerialNum(i)
|
||||||
.setData(data);
|
.setData(data);
|
||||||
|
@ -175,6 +183,8 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
batchId.setLength(i);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -182,24 +192,28 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, int startPosition, FutureCallback<Boolean> callback) {
|
public void postBatchData(BatchIdentifier batchId, List<BatchChunk> batchChunkList, int startPosition, FutureCallback<Boolean> callback)
|
||||||
postBatchData(ByteString.copyFrom(signerId), batchId, batchDataList, startPosition, 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
|
@Override
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
public void postBatchData(BatchIdentifier batchId, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback) throws IllegalArgumentException{
|
||||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
postBatchData(batchId, batchChunkList, 0, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, int startPosition, FutureCallback<Boolean> callback) {
|
|
||||||
Futures.addCallback(executorService.submit(new BatchDataPoster(signerId, batchId, batchDataList, startPosition)), callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
|
||||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class BatchCloser implements Callable<Boolean> {
|
private class BatchCloser implements Callable<Boolean> {
|
||||||
|
|
||||||
|
@ -212,14 +226,33 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean call() throws Exception {
|
public Boolean call() throws Exception {
|
||||||
return server.closeBatchMessage(msg).getValue();
|
return server.closeBatch(msg).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback<Boolean> callback) {
|
public void closeBatch(BatchIdentifier batchId, Timestamp timestamp, Iterable<Signature> signatures, FutureCallback<Boolean> 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);
|
Futures.addCallback(executorService.submit(new BatchCloser(closeBatchMessage)), callback);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RedundancyGetter implements Callable<Float> {
|
private class RedundancyGetter implements Callable<Float> {
|
||||||
|
@ -310,7 +343,8 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC
|
||||||
public void onSuccess(List<BulletinBoardMessage> result) {
|
public void onSuccess(List<BulletinBoardMessage> result) {
|
||||||
|
|
||||||
// Report new messages to user
|
// Report new messages to user
|
||||||
callback.onSuccess(result);
|
if (callback != null)
|
||||||
|
callback.onSuccess(result);
|
||||||
|
|
||||||
MessageFilterList.Builder filterBuilder = filterList.toBuilder();
|
MessageFilterList.Builder filterBuilder = filterList.toBuilder();
|
||||||
|
|
||||||
|
@ -331,7 +365,7 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC
|
||||||
filterList = filterBuilder.build();
|
filterList = filterBuilder.build();
|
||||||
|
|
||||||
// Reschedule job
|
// 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) {
|
public void onFailure(Throwable t) {
|
||||||
|
|
||||||
// Notify caller about failure and terminate subscription
|
// 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);
|
subscribe(filterList, 0, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CompleteBatchReader implements Callable<CompleteBatch> {
|
private class BatchDataReader implements Callable<List<BatchChunk>> {
|
||||||
|
|
||||||
private final BatchSpecificationMessage batchSpecificationMessage;
|
private final MessageID msgID;
|
||||||
|
|
||||||
public CompleteBatchReader(BatchSpecificationMessage batchSpecificationMessage) {
|
public BatchDataReader(MessageID msgID) {
|
||||||
this.batchSpecificationMessage = batchSpecificationMessage;
|
this.msgID = msgID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BatchChunk> call() throws Exception {
|
||||||
|
|
||||||
|
BatchQuery batchQuery = BatchQuery.newBuilder()
|
||||||
|
.setMsgID(msgID)
|
||||||
|
.setStartPosition(0)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
|
||||||
|
MessageOutputStream<BatchChunk> batchOutputStream = new MessageOutputStream<>(byteOutputStream);
|
||||||
|
server.readBatch(batchQuery,batchOutputStream);
|
||||||
|
|
||||||
|
MessageInputStream<BatchChunk> inputStream =
|
||||||
|
MessageInputStreamFactory.createMessageInputStream(
|
||||||
|
new ByteArrayInputStream(byteOutputStream.toByteArray()),
|
||||||
|
BatchChunk.class);
|
||||||
|
|
||||||
|
return inputStream.asList();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CompleteBatchReader implements Callable<BulletinBoardMessage> {
|
||||||
|
|
||||||
|
private final MessageID msgID;
|
||||||
|
|
||||||
|
public CompleteBatchReader(MessageID msgID) {
|
||||||
|
this.msgID = msgID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompleteBatch call() throws Exception {
|
public BulletinBoardMessage call() throws Exception {
|
||||||
|
|
||||||
final String[] TAGS_TO_REMOVE = {BulletinBoardConstants.BATCH_TAG, BulletinBoardConstants.BATCH_ID_TAG_PREFIX};
|
// Read message (mat be a stub)
|
||||||
|
|
||||||
CompleteBatch completeBatch = new CompleteBatch(BeginBatchMessage.newBuilder()
|
|
||||||
.setSignerId(batchSpecificationMessage.getSignerId())
|
|
||||||
.setBatchId(batchSpecificationMessage.getBatchId())
|
|
||||||
.build());
|
|
||||||
|
|
||||||
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
|
|
||||||
MessageOutputStream<BatchData> batchOutputStream = new MessageOutputStream<>(byteOutputStream);
|
|
||||||
server.readBatch(batchSpecificationMessage,batchOutputStream);
|
|
||||||
|
|
||||||
MessageInputStream<BatchData> batchInputStream =
|
|
||||||
MessageInputStreamFactory.createMessageInputStream(
|
|
||||||
new ByteArrayInputStream(byteOutputStream.toByteArray()),
|
|
||||||
BatchData.class);
|
|
||||||
|
|
||||||
completeBatch.appendBatchData(batchInputStream.asList());
|
|
||||||
|
|
||||||
MessageFilterList filterList = MessageFilterList.newBuilder()
|
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||||
.addFilter(MessageFilter.newBuilder()
|
.addFilter(MessageFilter.newBuilder()
|
||||||
.setType(FilterType.TAG)
|
.setType(FilterType.MSG_ID)
|
||||||
.setTag(BulletinBoardConstants.BATCH_TAG)
|
.setId(msgID.getID())
|
||||||
.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())
|
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
byteOutputStream = new ByteArrayOutputStream();
|
MessageReader messageReader = new MessageReader(filterList);
|
||||||
MessageOutputStream<BulletinBoardMessage> messageOutputStream = new MessageOutputStream<>(byteOutputStream);
|
List<BulletinBoardMessage> bulletinBoardMessages = messageReader.call();
|
||||||
server.readMessages(filterList,messageOutputStream);
|
|
||||||
|
|
||||||
MessageInputStream<BulletinBoardMessage> messageInputStream =
|
if (bulletinBoardMessages.size() <= 0) {
|
||||||
MessageInputStreamFactory.createMessageInputStream(
|
throw new NotFoundException("Message does not exist");
|
||||||
new ByteArrayInputStream(byteOutputStream.toByteArray()),
|
}
|
||||||
BulletinBoardMessage.class);
|
|
||||||
|
|
||||||
if (!messageInputStream.isAvailable())
|
BulletinBoardMessage msg = bulletinBoardMessages.get(0);
|
||||||
throw new NotFoundException("Batch does not exist");
|
|
||||||
|
|
||||||
BulletinBoardMessage message = messageInputStream.readMessage();
|
if (msg.getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.MSGID) {
|
||||||
|
|
||||||
completeBatch.setBeginBatchMessage(BeginBatchMessage.newBuilder()
|
// Read data
|
||||||
.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());
|
|
||||||
|
|
||||||
completeBatch.setSignature(message.getSig(0));
|
BatchDataReader batchDataReader = new BatchDataReader(msgID);
|
||||||
completeBatch.setTimestamp(message.getMsg().getTimestamp());
|
List<BatchChunk> batchChunkList = batchDataReader.call();
|
||||||
|
|
||||||
return completeBatch;
|
// Combine and return
|
||||||
|
|
||||||
|
return BulletinBoardUtils.gatherBatch(msg, batchChunkList);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BatchDataCombiner implements Callable<BulletinBoardMessage> {
|
||||||
|
|
||||||
|
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<BatchChunk> batchChunkList = batchDataReader.call();
|
||||||
|
|
||||||
|
return BulletinBoardUtils.gatherBatch(stub, batchChunkList);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback<CompleteBatch> callback) {
|
public void readMessage(MessageID msgID, FutureCallback<BulletinBoardMessage> callback) {
|
||||||
Futures.addCallback(executorService.submit(new CompleteBatchReader(batchSpecificationMessage)), callback);
|
Futures.addCallback(executorService.submit(new CompleteBatchReader(msgID)), callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readBatchData(BulletinBoardMessage stub, FutureCallback<BulletinBoardMessage> 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<SyncQueryResponse> {
|
private class SyncQueryHandler implements Callable<SyncQueryResponse> {
|
||||||
|
@ -474,17 +548,26 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC
|
||||||
@Override
|
@Override
|
||||||
public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
MessagePoster poster = new MessagePoster(msg);
|
MessagePoster poster = new MessagePoster(msg);
|
||||||
poster.call();
|
poster.call();
|
||||||
|
|
||||||
digest.update(msg);
|
digest.update(msg.getMsg());
|
||||||
return digest.digestAsMessageID();
|
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
|
@Override
|
||||||
public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) {
|
public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) throws CommunicationException{
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
@ -511,14 +594,89 @@ public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardC
|
||||||
return reader.call();
|
return reader.call();
|
||||||
|
|
||||||
} catch (Exception e){
|
} catch (Exception e){
|
||||||
return null;
|
throw new CommunicationException("Error reading from server");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SyncQuery generateSyncQuery(GenerateSyncQueryParams GenerateSyncQueryParams) throws CommunicationException {
|
public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException {
|
||||||
return server.generateSyncQuery(GenerateSyncQueryParams);
|
|
||||||
|
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<Boolean> 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<Boolean> 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
|
@Override
|
||||||
|
|
|
@ -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<BatchIdentifier> identifiers;
|
||||||
|
|
||||||
|
public MultiServerBatchIdentifier(Iterable<BatchIdentifier> identifiers) {
|
||||||
|
this.identifiers = identifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultiServerBatchIdentifier(BatchIdentifier[] identifiers) {
|
||||||
|
this.identifiers = Arrays.asList(identifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<BatchIdentifier> getIdentifiers() {
|
||||||
|
return identifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
*/
|
*/
|
||||||
public abstract class MultiServerWorker<IN, OUT> extends BulletinClientWorker<IN> implements Runnable, FutureCallback<OUT>{
|
public abstract class MultiServerWorker<IN, OUT> extends BulletinClientWorker<IN> implements Runnable, FutureCallback<OUT>{
|
||||||
|
|
||||||
private final List<SingleServerBulletinBoardClient> clients;
|
protected final List<SingleServerBulletinBoardClient> clients;
|
||||||
|
|
||||||
protected AtomicInteger minServers; // The minimal number of servers the job must be successful on for the job to be completed
|
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<IN, OUT> extends BulletinClientWorker<IN
|
||||||
*/
|
*/
|
||||||
protected void succeed(OUT result){
|
protected void succeed(OUT result){
|
||||||
if (returnedResult.compareAndSet(false, true)) {
|
if (returnedResult.compareAndSet(false, true)) {
|
||||||
futureCallback.onSuccess(result);
|
if (futureCallback != null)
|
||||||
|
futureCallback.onSuccess(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,18 +86,11 @@ public abstract class MultiServerWorker<IN, OUT> extends BulletinClientWorker<IN
|
||||||
*/
|
*/
|
||||||
protected void fail(Throwable t){
|
protected void fail(Throwable t){
|
||||||
if (returnedResult.compareAndSet(false, true)) {
|
if (returnedResult.compareAndSet(false, true)) {
|
||||||
futureCallback.onFailure(t);
|
if (futureCallback != null)
|
||||||
|
futureCallback.onFailure(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Used by implementations to get a Single Server Client iterator
|
|
||||||
* @return the requested iterator
|
|
||||||
*/
|
|
||||||
protected Iterator<SingleServerBulletinBoardClient> getClientIterator() {
|
|
||||||
return clients.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getClientNumber() {
|
protected int getClientNumber() {
|
||||||
return clients.size();
|
return clients.size();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,192 +1,329 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import com.google.protobuf.BoolValue;
|
import com.google.protobuf.BoolValue;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import meerkat.comm.CommunicationException;
|
import com.google.protobuf.Int64Value;
|
||||||
import meerkat.crypto.Digest;
|
import meerkat.bulletinboard.workers.singleserver.*;
|
||||||
import meerkat.crypto.concrete.SHA256Digest;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.crypto.concrete.SHA256Digest;
|
||||||
import meerkat.protobuf.Comm.*;
|
import meerkat.protobuf.BulletinBoardAPI;
|
||||||
import meerkat.protobuf.Voting.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.rest.*;
|
import meerkat.protobuf.Voting.*;
|
||||||
|
import meerkat.rest.*;
|
||||||
import java.util.List;
|
import meerkat.util.BulletinBoardUtils;
|
||||||
|
|
||||||
import javax.ws.rs.client.Client;
|
import java.util.List;
|
||||||
import javax.ws.rs.client.ClientBuilder;
|
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Client;
|
||||||
import javax.ws.rs.client.WebTarget;
|
import javax.ws.rs.client.ClientBuilder;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.client.Entity;
|
||||||
|
import javax.ws.rs.client.WebTarget;
|
||||||
import static meerkat.bulletinboard.BulletinBoardConstants.*;
|
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
|
/**
|
||||||
*/
|
* Created by Arbel Deutsch Peled on 05-Dec-15.
|
||||||
public class SimpleBulletinBoardClient implements BulletinBoardClient{
|
* Implements BulletinBoardClient interface in a simple, straightforward manner
|
||||||
|
*/
|
||||||
protected List<String> meerkatDBs;
|
public class SimpleBulletinBoardClient implements BulletinBoardClient{
|
||||||
|
|
||||||
protected Client client;
|
protected List<String> meerkatDBs;
|
||||||
|
|
||||||
protected Digest digest;
|
protected Client client;
|
||||||
|
|
||||||
/**
|
protected BulletinBoardDigest digest;
|
||||||
* Stores database locations and initializes the web Client
|
|
||||||
* @param clientParams contains the data needed to access the DBs
|
/**
|
||||||
*/
|
* Stores database locations and initializes the web Client
|
||||||
@Override
|
* @param clientParams contains the data needed to access the DBs
|
||||||
public void init(BulletinBoardClientParams clientParams) {
|
*/
|
||||||
|
@Override
|
||||||
this.meerkatDBs = clientParams.getBulletinBoardAddressList();
|
public void init(BulletinBoardClientParams clientParams) {
|
||||||
|
|
||||||
client = ClientBuilder.newClient();
|
this.meerkatDBs = clientParams.getBulletinBoardAddressList();
|
||||||
client.register(ProtobufMessageBodyReader.class);
|
|
||||||
client.register(ProtobufMessageBodyWriter.class);
|
client = ClientBuilder.newClient();
|
||||||
|
client.register(ProtobufMessageBodyReader.class);
|
||||||
digest = new SHA256Digest();
|
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
|
* Post message to all DBs
|
||||||
* @throws CommunicationException
|
* Make only one try per DB.
|
||||||
*/
|
* @param msg is the message,
|
||||||
@Override
|
* @return the message ID for later retrieval
|
||||||
public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
* @throws CommunicationException
|
||||||
|
*/
|
||||||
WebTarget webTarget;
|
@Override
|
||||||
Response response;
|
public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||||
|
|
||||||
// Post message to all databases
|
WebTarget webTarget;
|
||||||
try {
|
Response response = null;
|
||||||
for (String db : meerkatDBs) {
|
|
||||||
webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(POST_MESSAGE_PATH);
|
// Post message to all databases
|
||||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
try {
|
||||||
|
for (String db : meerkatDBs) {
|
||||||
// Only consider valid responses
|
|
||||||
if (response.getStatusInfo() == Response.Status.OK
|
SingleServerPostMessageWorker worker = new SingleServerPostMessageWorker(db, msg, 0);
|
||||||
|| response.getStatusInfo() == Response.Status.CREATED) {
|
|
||||||
response.readEntity(BoolValue.class).getValue();
|
worker.call();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) { // Occurs only when server replies with valid status but invalid data
|
} catch (Exception e) { // Occurs only when server replies with valid status but invalid data
|
||||||
throw new CommunicationException("Error accessing database: " + e.getMessage());
|
throw new CommunicationException("Error accessing database: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the correct message ID and return it
|
// Calculate the correct message ID and return it
|
||||||
digest.reset();
|
digest.reset();
|
||||||
digest.update(msg.getMsg());
|
digest.update(msg.getMsg());
|
||||||
return MessageID.newBuilder().setID(ByteString.copyFrom(digest.digest())).build();
|
return MessageID.newBuilder().setID(ByteString.copyFrom(digest.digest())).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Access each database and search for a given message ID
|
* Access each database and search for a given message ID
|
||||||
* Return the number of databases in which the message was found
|
* Return the number of databases in which the message was found
|
||||||
* Only try once per DB
|
* Only try once per DB
|
||||||
* Ignore communication exceptions in specific databases
|
* Ignore communication exceptions in specific databases
|
||||||
* @param id is the requested message ID
|
* @param id is the requested message ID
|
||||||
* @return the number of DBs in which retrieval was successful
|
* @return the number of DBs in which retrieval was successful
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public float getRedundancy(MessageID id) {
|
public float getRedundancy(MessageID id) {
|
||||||
WebTarget webTarget;
|
WebTarget webTarget;
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
MessageFilterList filterList = MessageFilterList.newBuilder()
|
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||||
.addFilter(MessageFilter.newBuilder()
|
.addFilter(MessageFilter.newBuilder()
|
||||||
.setType(FilterType.MSG_ID)
|
.setType(FilterType.MSG_ID)
|
||||||
.setId(id.getID())
|
.setId(id.getID())
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
float count = 0;
|
float count = 0;
|
||||||
|
|
||||||
for (String db : meerkatDBs) {
|
for (String db : meerkatDBs) {
|
||||||
try {
|
try {
|
||||||
webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH);
|
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));
|
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF));
|
||||||
|
|
||||||
if (response.readEntity(BulletinBoardMessageList.class).getMessageCount() > 0){
|
if (response.readEntity(BulletinBoardMessageList.class).getMessageCount() > 0){
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {}
|
} catch (Exception e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
return count / ((float) meerkatDBs.size());
|
return count / ((float) meerkatDBs.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Go through the DBs and try to retrieve messages according to the specified filter
|
* 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 at the operation is successful for some DB: return the results and stop iterating
|
||||||
* If no operation is successful: return null (NOT blank list)
|
* If no operation is successful: return null (NOT blank list)
|
||||||
* @param filterList return only messages that match the filters (null means no filtering).
|
* @param filterList return only messages that match the filters (null means no filtering).
|
||||||
* @return the list of Bulletin Board messages that are returned from a server
|
* @return the list of Bulletin Board messages that are returned from a server
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) {
|
public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) throws CommunicationException{
|
||||||
|
|
||||||
WebTarget webTarget;
|
// Replace null filter list with blank one.
|
||||||
Response response;
|
if (filterList == null){
|
||||||
BulletinBoardMessageList messageList;
|
filterList = MessageFilterList.getDefaultInstance();
|
||||||
|
}
|
||||||
// Replace null filter list with blank one.
|
|
||||||
if (filterList == null){
|
String exceptionString = "";
|
||||||
filterList = MessageFilterList.newBuilder().build();
|
|
||||||
}
|
for (String db : meerkatDBs) {
|
||||||
|
|
||||||
for (String db : meerkatDBs) {
|
try {
|
||||||
|
|
||||||
try {
|
SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(db, filterList, 0);
|
||||||
webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH);
|
|
||||||
|
List<BulletinBoardMessage> result = worker.call();
|
||||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF));
|
|
||||||
|
return result;
|
||||||
messageList = response.readEntity(BulletinBoardMessageList.class);
|
|
||||||
|
} catch (Exception e) {
|
||||||
if (messageList != null){
|
//TODO: log
|
||||||
return messageList.getMessageList();
|
exceptionString += e.getMessage() + "\n";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {}
|
|
||||||
|
throw new CommunicationException("Could not find message in any DB. Errors follow:\n" + exceptionString);
|
||||||
}
|
|
||||||
|
}
|
||||||
return null;
|
|
||||||
|
@Override
|
||||||
}
|
public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException {
|
||||||
|
|
||||||
@Override
|
List<BatchChunk> chunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize);
|
||||||
public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException {
|
|
||||||
|
BeginBatchMessage beginBatchMessage = BulletinBoardUtils.generateBeginBatchMessage(msg);
|
||||||
WebTarget webTarget;
|
|
||||||
Response response;
|
boolean posted = false;
|
||||||
|
|
||||||
for (String db : meerkatDBs) {
|
// Post message to all databases
|
||||||
|
|
||||||
try {
|
for (String db : meerkatDBs) {
|
||||||
webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(GENERATE_SYNC_QUERY_PATH);
|
|
||||||
|
try {
|
||||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(generateSyncQueryParams, Constants.MEDIATYPE_PROTOBUF));
|
|
||||||
|
int pos = 0;
|
||||||
return response.readEntity(SyncQuery.class);
|
|
||||||
|
SingleServerBeginBatchWorker beginBatchWorker = new SingleServerBeginBatchWorker(db, beginBatchMessage, 0);
|
||||||
} catch (Exception e) {}
|
|
||||||
|
Int64Value batchId = beginBatchWorker.call();
|
||||||
}
|
|
||||||
|
BatchMessage.Builder builder = BatchMessage.newBuilder().setBatchId(batchId.getValue());
|
||||||
throw new CommunicationException("Could not contact any server");
|
|
||||||
|
for (BatchChunk batchChunk : chunkList) {
|
||||||
}
|
|
||||||
|
SingleServerPostBatchWorker postBatchWorker =
|
||||||
public void close() {
|
new SingleServerPostBatchWorker(
|
||||||
client.close();
|
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<BulletinBoardMessage> messages = messagesWorker.call();
|
||||||
|
|
||||||
|
if (messages == null || messages.size() < 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
BulletinBoardMessage stub = messages.get(0);
|
||||||
|
|
||||||
|
SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(db, batchQuery, 0);
|
||||||
|
|
||||||
|
List<BatchChunk> 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<BatchChunk> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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<FutureCallback<Integer>> messageCountCallbacks;
|
||||||
|
private List<FutureCallback<SyncStatus>> 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<List<BulletinBoardMessage>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<BulletinBoardMessage> result) {
|
||||||
|
|
||||||
|
// Notify Message Count callbacks if needed
|
||||||
|
|
||||||
|
if (syncStatus != SyncStatus.SYNCHRONIZED || result.size() > 0) {
|
||||||
|
|
||||||
|
for (FutureCallback<Integer> callback : messageCountCallbacks){
|
||||||
|
callback.onSuccess(result.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle upload and status change
|
||||||
|
|
||||||
|
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<SyncStatus> 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<SyncStatus> callback) {
|
||||||
|
syncStatusCallbacks.add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BulletinBoardMessage> getRemainingMessages() throws CommunicationException{
|
||||||
|
return localClient.readMessages(EMPTY_FILTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getRemainingMessages(FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||||
|
localClient.readMessages(EMPTY_FILTER, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getRemainingMessagesCount() throws CommunicationException {
|
||||||
|
return localClient.readMessages(EMPTY_FILTER).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void subscribeToRemainingMessagesCount(FutureCallback<Integer> 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,16 +4,19 @@ import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
|
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
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.bulletinboard.workers.singleserver.*;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
|
import meerkat.crypto.concrete.SHA256Digest;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
import meerkat.protobuf.Crypto;
|
||||||
import meerkat.protobuf.Voting.BulletinBoardClientParams;
|
import meerkat.protobuf.Voting.BulletinBoardClientParams;
|
||||||
import meerkat.util.BulletinBoardUtils;
|
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.Iterator;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
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
|
* 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
|
* 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 final int MAX_RETRIES = 11;
|
||||||
|
|
||||||
private ListeningScheduledExecutorService executorService;
|
private final ListeningScheduledExecutorService executorService;
|
||||||
|
|
||||||
protected BatchDigest batchDigest;
|
|
||||||
|
|
||||||
private long lastServerErrorTime;
|
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
|
* Notify the client that a job has failed
|
||||||
|
@ -53,6 +60,43 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class SynchronousRetry<OUT> {
|
||||||
|
|
||||||
|
private final SingleServerWorker<?,OUT> worker;
|
||||||
|
|
||||||
|
private String thrown;
|
||||||
|
|
||||||
|
public SynchronousRetry(SingleServerWorker<?,OUT> 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
|
* 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
|
* 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;
|
long timeSinceLastServerError = System.currentTimeMillis() - lastServerErrorTime;
|
||||||
|
|
||||||
if (timeSinceLastServerError >= failDelayInMilliseconds) {
|
if (timeSinceLastServerError >= FAIL_DELAY_IN_MILLISECONDS) {
|
||||||
|
|
||||||
// Schedule for immediate processing
|
// Schedule for immediate processing
|
||||||
Futures.addCallback(executorService.submit(worker), callback);
|
Futures.addCallback(executorService.submit(worker), callback);
|
||||||
|
@ -74,7 +118,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
// Schedule for processing immediately following delay expiry
|
// Schedule for processing immediately following delay expiry
|
||||||
Futures.addCallback(executorService.schedule(
|
Futures.addCallback(executorService.schedule(
|
||||||
worker,
|
worker,
|
||||||
failDelayInMilliseconds - timeSinceLastServerError,
|
FAIL_DELAY_IN_MILLISECONDS - timeSinceLastServerError,
|
||||||
TimeUnit.MILLISECONDS),
|
TimeUnit.MILLISECONDS),
|
||||||
callback);
|
callback);
|
||||||
|
|
||||||
|
@ -97,7 +141,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(T result) {
|
public void onSuccess(T result) {
|
||||||
futureCallback.onSuccess(result);
|
if (futureCallback != null)
|
||||||
|
futureCallback.onSuccess(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -115,7 +160,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
scheduleWorker(worker, this);
|
scheduleWorker(worker, this);
|
||||||
} else {
|
} else {
|
||||||
// No more retries: notify caller about failure
|
// 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
|
* 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
|
* If any batch-data fails to post: this callback reports failure
|
||||||
*/
|
*/
|
||||||
class PostBatchDataListCallback implements FutureCallback<Boolean> {
|
class PostBatchChunkListCallback implements FutureCallback<Boolean> {
|
||||||
|
|
||||||
private final FutureCallback<Boolean> callback;
|
private final FutureCallback<Boolean> callback;
|
||||||
|
|
||||||
private AtomicInteger batchDataRemaining;
|
private AtomicInteger batchDataRemaining;
|
||||||
private AtomicBoolean aggregatedResult;
|
private AtomicBoolean aggregatedResult;
|
||||||
|
|
||||||
public PostBatchDataListCallback(int batchDataLength, FutureCallback<Boolean> callback) {
|
public PostBatchChunkListCallback(int batchDataLength, FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.batchDataRemaining = new AtomicInteger(batchDataLength);
|
this.batchDataRemaining = new AtomicInteger(batchDataLength);
|
||||||
|
@ -150,7 +196,8 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batchDataRemaining.decrementAndGet() == 0){
|
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) {
|
public void onFailure(Throwable t) {
|
||||||
|
|
||||||
// Notify caller about failure
|
// Notify caller about failure
|
||||||
callback.onFailure(t);
|
if (callback != null)
|
||||||
|
callback.onFailure(t);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ReadBatchCallback implements FutureCallback<List<BatchChunk>> {
|
||||||
|
|
||||||
|
private final BulletinBoardMessage stub;
|
||||||
|
private final FutureCallback<BulletinBoardMessage> callback;
|
||||||
|
|
||||||
|
public ReadBatchCallback(BulletinBoardMessage stub, FutureCallback<BulletinBoardMessage> callback) {
|
||||||
|
this.stub = stub;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<BatchChunk> result) {
|
||||||
|
callback.onSuccess(BulletinBoardUtils.gatherBatch(stub, result));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable t) {
|
||||||
|
callback.onFailure(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This callback ties together the different parts of a CompleteBatch as they arrive from the server
|
* This callback receives a message which may be a stub
|
||||||
* It assembles a CompleteBatch from the parts and sends it to the user if all parts arrived
|
* If the message is not a stub: it returns it as is to a callback function
|
||||||
* If any part fails to arrive: it invokes the onFailure method
|
* 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<List<BulletinBoardMessage>>{
|
||||||
|
|
||||||
private final FutureCallback<CompleteBatch> callback;
|
private final FutureCallback<BulletinBoardMessage> callback;
|
||||||
|
|
||||||
private List<BatchData> batchDataList;
|
public CompleteMessageReadCallback(FutureCallback<BulletinBoardMessage> callback) {
|
||||||
private BulletinBoardMessage batchMessage;
|
|
||||||
|
|
||||||
private AtomicInteger remainingQueries;
|
|
||||||
private AtomicBoolean failed;
|
|
||||||
|
|
||||||
public CompleteBatchReadCallback(FutureCallback<CompleteBatch> callback) {
|
|
||||||
|
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
|
||||||
remainingQueries = new AtomicInteger(2);
|
|
||||||
failed = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void combineAndReturn() {
|
@Override
|
||||||
|
public void onSuccess(List<BulletinBoardMessage> result) {
|
||||||
|
if (result.size() <= 0) {
|
||||||
|
onFailure(new CommunicationException("Could not find required message on the server."));
|
||||||
|
} else {
|
||||||
|
|
||||||
final String[] prefixes = {
|
BulletinBoardMessage msg = result.get(0);
|
||||||
BulletinBoardConstants.BATCH_ID_TAG_PREFIX,
|
|
||||||
BulletinBoardConstants.BATCH_TAG};
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* @return a FutureCallback for the Batch Data List that ties to this object
|
public void onFailure(Throwable t) {
|
||||||
*/
|
callback.onFailure(t);
|
||||||
public FutureCallback<List<BatchData>> asBatchDataListFutureCallback() {
|
|
||||||
return new FutureCallback<List<BatchData>>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuccess(List<BatchData> 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<List<BulletinBoardMessage>> asBulletinBoardMessageListFutureCallback() {
|
|
||||||
return new FutureCallback<List<BulletinBoardMessage>>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuccess(List<BulletinBoardMessage> 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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -289,22 +306,31 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
public void onSuccess(List<BulletinBoardMessage> result) {
|
public void onSuccess(List<BulletinBoardMessage> result) {
|
||||||
|
|
||||||
// Report new messages to user
|
// Report new messages to user
|
||||||
callback.onSuccess(result);
|
if (callback != null)
|
||||||
|
callback.onSuccess(result);
|
||||||
|
|
||||||
// Remove last filter from list (MIN_ENTRY one)
|
// Update filter if needed
|
||||||
filterBuilder.removeFilter(filterBuilder.getFilterCount() - 1);
|
|
||||||
|
|
||||||
// Add updated MIN_ENTRY filter (entry number is successor of last received entry's number)
|
if (result.size() > 0) {
|
||||||
filterBuilder.addFilter(MessageFilter.newBuilder()
|
|
||||||
.setType(FilterType.MIN_ENTRY)
|
// Remove last filter from list (MIN_ENTRY one)
|
||||||
.setEntry(result.get(result.size() - 1).getEntryNum() + 1)
|
filterBuilder.removeFilter(filterBuilder.getFilterCount() - 1);
|
||||||
.build());
|
|
||||||
|
// 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
|
// 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
|
RetryCallback<List<BulletinBoardMessage>> retryCallback = new RetryCallback<>(worker, this);
|
||||||
scheduleWorker(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();
|
fail();
|
||||||
|
|
||||||
// Notify caller about failure and terminate subscription
|
// 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.executorService = executorService;
|
||||||
|
|
||||||
this.failDelayInMilliseconds = failDelayInMilliseconds;
|
this.FAIL_DELAY_IN_MILLISECONDS = failDelayInMilliseconds;
|
||||||
this.subscriptionIntervalInMilliseconds = subscriptionIntervalInMilliseconds;
|
this.SUBSCRIPTION_INTERVAL_IN_MILLISECONDS = subscriptionIntervalInMilliseconds;
|
||||||
|
|
||||||
// Set server error time to a time sufficiently in the past to make new jobs go through
|
// Set server error time to a time sufficiently in the past to make new jobs go through
|
||||||
lastServerErrorTime = System.currentTimeMillis() - failDelayInMilliseconds;
|
lastServerErrorTime = System.currentTimeMillis() - failDelayInMilliseconds;
|
||||||
|
@ -348,79 +375,289 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
@Override
|
@Override
|
||||||
public void init(BulletinBoardClientParams clientParams) {
|
public void init(BulletinBoardClientParams clientParams) {
|
||||||
|
|
||||||
// Perform usual setup
|
this.digest = new GenericBulletinBoardDigest(new SHA256Digest());
|
||||||
super.init(clientParams);
|
|
||||||
|
|
||||||
// Wrap the Digest into a BatchDigest
|
|
||||||
batchDigest = new GenericBatchDigest(digest);
|
|
||||||
|
|
||||||
// Remove all but first DB address
|
// Remove all but first DB address
|
||||||
String dbAddress = meerkatDBs.get(0);
|
this.dbAddress = clientParams.getBulletinBoardAddress(0);
|
||||||
meerkatDBs = new LinkedList<>();
|
|
||||||
meerkatDBs.add(dbAddress);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Synchronous methods
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||||
|
|
||||||
|
SingleServerPostMessageWorker worker = new SingleServerPostMessageWorker(dbAddress, msg, MAX_RETRIES);
|
||||||
|
|
||||||
|
SynchronousRetry<Boolean> 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<Float> retry = new SynchronousRetry<>(worker);
|
||||||
|
|
||||||
|
return retry.run();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) throws CommunicationException {
|
||||||
|
|
||||||
|
SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(dbAddress, filterList, MAX_RETRIES);
|
||||||
|
|
||||||
|
SynchronousRetry<List<BulletinBoardMessage>> 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<Int64Value> beginRetry = new SynchronousRetry<>(beginBatchWorker);
|
||||||
|
|
||||||
|
Int64Value identifier = beginRetry.run();
|
||||||
|
|
||||||
|
// Post data chunks
|
||||||
|
|
||||||
|
List<BatchChunk> 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<Boolean> 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<Boolean> 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<List<BulletinBoardMessage>> retry = new SynchronousRetry<>(stubWorker);
|
||||||
|
|
||||||
|
List<BulletinBoardMessage> 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<List<BatchChunk>> batchRetry = new SynchronousRetry<>(readBatchWorker);
|
||||||
|
|
||||||
|
List<BatchChunk> 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<SyncQuery> retry = new SynchronousRetry<>(worker);
|
||||||
|
|
||||||
|
return retry.run();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asynchronous methods
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageID postMessage(BulletinBoardMessage msg, FutureCallback<Boolean> callback) {
|
public MessageID postMessage(BulletinBoardMessage msg, FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
// Create worker with redundancy 1 and MAX_RETRIES retries
|
// 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
|
// Submit worker and create callback
|
||||||
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
||||||
|
|
||||||
// Calculate the correct message ID and return it
|
// Calculate the correct message ID and return it
|
||||||
batchDigest.reset();
|
digest.reset();
|
||||||
batchDigest.update(msg.getMsg());
|
digest.update(msg.getMsg());
|
||||||
return batchDigest.digestAsMessageID();
|
return digest.digestAsMessageID();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PostBatchDataCallback implements FutureCallback<Boolean> {
|
private class PostBatchDataCallback implements FutureCallback<Boolean> {
|
||||||
|
|
||||||
private final CompleteBatch completeBatch;
|
private final BulletinBoardMessage msg;
|
||||||
|
private final BatchIdentifier identifier;
|
||||||
private final FutureCallback<Boolean> callback;
|
private final FutureCallback<Boolean> callback;
|
||||||
|
|
||||||
public PostBatchDataCallback(CompleteBatch completeBatch, FutureCallback<Boolean> callback) {
|
public PostBatchDataCallback(BulletinBoardMessage msg, BatchIdentifier identifier, FutureCallback<Boolean> callback) {
|
||||||
this.completeBatch = completeBatch;
|
this.msg = msg;
|
||||||
|
this.identifier = identifier;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Boolean msg) {
|
public void onSuccess(Boolean result) {
|
||||||
closeBatch(
|
closeBatch(
|
||||||
completeBatch.getCloseBatchMessage(),
|
identifier,
|
||||||
|
msg.getMsg().getTimestamp(),
|
||||||
|
msg.getSigList(),
|
||||||
callback
|
callback
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Throwable t) {
|
public void onFailure(Throwable t) {
|
||||||
callback.onFailure(t);
|
if (callback != null)
|
||||||
|
callback.onFailure(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BeginBatchCallback implements FutureCallback<Boolean> {
|
private class ContinueBatchCallback implements FutureCallback<BatchIdentifier> {
|
||||||
|
|
||||||
private final CompleteBatch completeBatch;
|
private final BulletinBoardMessage msg;
|
||||||
|
private final int chunkSize;
|
||||||
private final FutureCallback<Boolean> callback;
|
private final FutureCallback<Boolean> callback;
|
||||||
|
|
||||||
public BeginBatchCallback(CompleteBatch completeBatch, FutureCallback<Boolean> callback) {
|
public ContinueBatchCallback(BulletinBoardMessage msg, int chunkSize, FutureCallback<Boolean> callback) {
|
||||||
this.completeBatch = completeBatch;
|
this.msg = msg;
|
||||||
|
this.chunkSize = chunkSize;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Boolean msg) {
|
public void onSuccess(BatchIdentifier identifier) {
|
||||||
|
|
||||||
|
List<BatchChunk> batchChunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize);
|
||||||
|
|
||||||
postBatchData(
|
postBatchData(
|
||||||
completeBatch.getBeginBatchMessage().getSignerId(),
|
identifier,
|
||||||
completeBatch.getBeginBatchMessage().getBatchId(),
|
batchChunkList,
|
||||||
completeBatch.getBatchDataList(),
|
|
||||||
0,
|
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<Boolean> callback) {
|
||||||
|
|
||||||
|
beginBatch(
|
||||||
|
msg.getMsg().getTagList(),
|
||||||
|
new ContinueBatchCallback(msg, chunkSize, callback)
|
||||||
|
);
|
||||||
|
|
||||||
|
digest.update(msg);
|
||||||
|
|
||||||
|
return digest.digestAsMessageID();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BeginBatchCallback implements FutureCallback<Int64Value> {
|
||||||
|
|
||||||
|
private final FutureCallback<BatchIdentifier> callback;
|
||||||
|
|
||||||
|
public BeginBatchCallback(FutureCallback<BatchIdentifier> callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Int64Value result) {
|
||||||
|
callback.onSuccess(new SingleServerBatchIdentifier(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -430,51 +667,53 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageID postBatch(CompleteBatch completeBatch, FutureCallback<Boolean> callback) {
|
public void beginBatch(Iterable<String> tags, FutureCallback<BatchIdentifier> callback) {
|
||||||
|
|
||||||
beginBatch(
|
BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder()
|
||||||
completeBatch.getBeginBatchMessage(),
|
.addAllTag(tags)
|
||||||
new BeginBatchCallback(completeBatch, callback)
|
.build();
|
||||||
);
|
|
||||||
|
|
||||||
batchDigest.update(completeBatch);
|
|
||||||
|
|
||||||
return batchDigest.digestAsMessageID();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback<Boolean> callback) {
|
|
||||||
|
|
||||||
// Create worker with redundancy 1 and MAX_RETRIES retries
|
// Create worker with redundancy 1 and MAX_RETRIES retries
|
||||||
SingleServerBeginBatchWorker worker =
|
SingleServerBeginBatchWorker worker =
|
||||||
new SingleServerBeginBatchWorker(meerkatDBs.get(0), beginBatchMessage, MAX_RETRIES);
|
new SingleServerBeginBatchWorker(dbAddress, beginBatchMessage, MAX_RETRIES);
|
||||||
|
|
||||||
// Submit worker and create callback
|
// 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<BatchData> batchDataList,
|
|
||||||
int startPosition, FutureCallback<Boolean> callback) {
|
|
||||||
|
|
||||||
BatchMessage.Builder builder = BatchMessage.newBuilder()
|
@Override
|
||||||
.setSignerId(signerId)
|
public void postBatchData(BatchIdentifier batchIdentifier, List<BatchChunk> batchChunkList,
|
||||||
.setBatchId(batchId);
|
int startPosition, FutureCallback<Boolean> 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
|
// 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
|
// 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);
|
builder.setSerialNum(startPosition).setData(data);
|
||||||
|
|
||||||
// Create worker with redundancy 1 and MAX_RETRIES retries
|
// Create worker with redundancy 1 and MAX_RETRIES retries
|
||||||
SingleServerPostBatchWorker worker =
|
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
|
// Create worker with redundancy 1 and MAX_RETRIES retries
|
||||||
scheduleWorker(worker, new RetryCallback<>(worker, listCallback));
|
scheduleWorker(worker, new RetryCallback<>(worker, listCallback));
|
||||||
|
@ -486,33 +725,33 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
public void postBatchData(BatchIdentifier batchIdentifier, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
postBatchData(batchIdentifier, batchChunkList, 0, callback);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList,
|
public void closeBatch(BatchIdentifier batchIdentifier, Timestamp timestamp, Iterable<Crypto.Signature> signatures, FutureCallback<Boolean> callback)
|
||||||
int startPosition, FutureCallback<Boolean> 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
|
CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder()
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
.setBatchId(identifier.getBatchId().getValue())
|
||||||
|
.setBatchLength(identifier.getLength())
|
||||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
.setTimestamp(timestamp)
|
||||||
|
.addAllSig(signatures)
|
||||||
}
|
.build();
|
||||||
|
|
||||||
@Override
|
|
||||||
public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback<Boolean> callback) {
|
|
||||||
|
|
||||||
// Create worker with redundancy 1 and MAX_RETRIES retries
|
// Create worker with redundancy 1 and MAX_RETRIES retries
|
||||||
SingleServerCloseBatchWorker worker =
|
SingleServerCloseBatchWorker worker =
|
||||||
new SingleServerCloseBatchWorker(meerkatDBs.get(0), closeBatchMessage, MAX_RETRIES);
|
new SingleServerCloseBatchWorker(dbAddress, closeBatchMessage, MAX_RETRIES);
|
||||||
|
|
||||||
// Submit worker and create callback
|
// Submit worker and create callback
|
||||||
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
||||||
|
@ -523,7 +762,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
public void getRedundancy(MessageID id, FutureCallback<Float> callback) {
|
public void getRedundancy(MessageID id, FutureCallback<Float> callback) {
|
||||||
|
|
||||||
// Create worker with no retries
|
// 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
|
// Submit job and create callback
|
||||||
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
||||||
|
@ -534,7 +773,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||||
|
|
||||||
// Create job with no retries
|
// 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
|
// Submit job and create callback
|
||||||
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
||||||
|
@ -542,43 +781,55 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback<CompleteBatch> callback) {
|
public void readMessage(MessageID msgID, FutureCallback<BulletinBoardMessage> 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()
|
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||||
.addFilter(MessageFilter.newBuilder()
|
.addFilter(MessageFilter.newBuilder()
|
||||||
.setType(FilterType.TAG)
|
.setType(FilterType.MSG_ID)
|
||||||
.setTag(BulletinBoardConstants.BATCH_TAG)
|
.setId(msgID.getID())
|
||||||
.build())
|
|
||||||
.addFilter(MessageFilter.newBuilder()
|
|
||||||
.setType(FilterType.TAG)
|
|
||||||
.setTag(BulletinBoardConstants.BATCH_ID_TAG_PREFIX + batchSpecificationMessage.getBatchId())
|
|
||||||
.build())
|
|
||||||
.addFilter(MessageFilter.newBuilder()
|
|
||||||
.setType(FilterType.SIGNER_ID)
|
|
||||||
.setId(batchSpecificationMessage.getSignerId())
|
|
||||||
.build())
|
.build())
|
||||||
.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
|
SingleServerReadMessagesWorker messageWorker = new SingleServerReadMessagesWorker(dbAddress, filterList, MAX_RETRIES);
|
||||||
SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(meerkatDBs.get(0), batchSpecificationMessage, 1);
|
|
||||||
|
|
||||||
// Create callback that will combine the two worker products
|
|
||||||
CompleteBatchReadCallback completeBatchReadCallback = new CompleteBatchReadCallback(callback);
|
|
||||||
|
|
||||||
// Submit jobs with wrapped callbacks
|
// Submit jobs with wrapped callbacks
|
||||||
scheduleWorker(messageWorker, new RetryCallback<>(messageWorker, completeBatchReadCallback.asBulletinBoardMessageListFutureCallback()));
|
scheduleWorker(messageWorker, new RetryCallback<>(messageWorker, new CompleteMessageReadCallback(callback)));
|
||||||
scheduleWorker(batchWorker, new RetryCallback<>(batchWorker, completeBatchReadCallback.asBatchDataListFutureCallback()));
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readBatchData(BulletinBoardMessage stub, FutureCallback<BulletinBoardMessage> 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
|
@Override
|
||||||
public void querySync(SyncQuery syncQuery, FutureCallback<SyncQueryResponse> callback) {
|
public void querySync(SyncQuery syncQuery, FutureCallback<SyncQueryResponse> 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));
|
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
||||||
|
|
||||||
|
@ -604,7 +855,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
// Create job with no retries
|
// 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
|
// Submit job and create callback that retries on failure and handles repeated subscription
|
||||||
scheduleWorker(worker, new RetryCallback<>(worker, new SubscriptionCallback(worker, callback)));
|
scheduleWorker(worker, new RetryCallback<>(worker, new SubscriptionCallback(worker, callback)));
|
||||||
|
@ -618,8 +869,6 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
||||||
super.close();
|
|
||||||
|
|
||||||
executorService.shutdown();
|
executorService.shutdown();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,255 +1,286 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
|
import com.google.protobuf.Timestamp;
|
||||||
import meerkat.bulletinboard.workers.multiserver.*;
|
import meerkat.bulletinboard.workers.multiserver.*;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.protobuf.BulletinBoardAPI;
|
import meerkat.protobuf.Crypto.Signature;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.Voting.*;
|
||||||
import meerkat.protobuf.Voting.*;
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.ArrayList;
|
import java.util.List;
|
||||||
import java.util.List;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
/**
|
||||||
/**
|
* Created by Arbel Deutsch Peled on 05-Dec-15.
|
||||||
* Created by Arbel Deutsch Peled on 05-Dec-15.
|
* Thread-based implementation of a Async Bulletin Board Client.
|
||||||
* Thread-based implementation of a Async Bulletin Board Client.
|
* Features:
|
||||||
* Features:
|
* 1. Handles tasks concurrently.
|
||||||
* 1. Handles tasks concurrently.
|
* 2. Retries submitting
|
||||||
* 2. Retries submitting
|
*/
|
||||||
*/
|
public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient implements AsyncBulletinBoardClient {
|
||||||
public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient implements AsyncBulletinBoardClient {
|
|
||||||
|
// Executor service for handling jobs
|
||||||
// Executor service for handling jobs
|
private final static int JOBS_THREAD_NUM = 5;
|
||||||
private final static int JOBS_THREAD_NUM = 5;
|
private ExecutorService executorService;
|
||||||
private ExecutorService executorService;
|
|
||||||
|
// Per-server clients
|
||||||
// Per-server clients
|
private List<SingleServerBulletinBoardClient> clients;
|
||||||
private List<SingleServerBulletinBoardClient> clients;
|
|
||||||
|
private BulletinBoardDigest batchDigest;
|
||||||
private BatchDigest batchDigest;
|
|
||||||
|
private final static int POST_MESSAGE_RETRY_NUM = 3;
|
||||||
private final static int POST_MESSAGE_RETRY_NUM = 3;
|
private final static int READ_MESSAGES_RETRY_NUM = 1;
|
||||||
private final static int READ_MESSAGES_RETRY_NUM = 1;
|
private final static int GET_REDUNDANCY_RETRY_NUM = 1;
|
||||||
private final static int GET_REDUNDANCY_RETRY_NUM = 1;
|
|
||||||
|
private final int SERVER_THREADPOOL_SIZE;
|
||||||
private static final int SERVER_THREADPOOL_SIZE = 5;
|
private final long FAIL_DELAY;
|
||||||
private static final long FAIL_DELAY = 5000;
|
private final long SUBSCRIPTION_INTERVAL;
|
||||||
private static final long SUBSCRIPTION_INTERVAL = 10000;
|
|
||||||
|
private static final int DEFAULT_SERVER_THREADPOOL_SIZE = 5;
|
||||||
private int minAbsoluteRedundancy;
|
private static final long DEFAULT_FAIL_DELAY = 5000;
|
||||||
|
private static final long DEFAULT_SUBSCRIPTION_INTERVAL = 10000;
|
||||||
/**
|
|
||||||
* Stores database locations and initializes the web Client
|
private int minAbsoluteRedundancy;
|
||||||
* Stores the required minimum redundancy.
|
|
||||||
* Starts the Thread Pool.
|
|
||||||
* @param clientParams contains the required information
|
public ThreadedBulletinBoardClient(int serverThreadpoolSize, long failDelay, long subscriptionInterval) {
|
||||||
*/
|
SERVER_THREADPOOL_SIZE = serverThreadpoolSize;
|
||||||
@Override
|
FAIL_DELAY = failDelay;
|
||||||
public void init(BulletinBoardClientParams clientParams) {
|
SUBSCRIPTION_INTERVAL = subscriptionInterval;
|
||||||
|
}
|
||||||
super.init(clientParams);
|
|
||||||
|
public ThreadedBulletinBoardClient() {
|
||||||
batchDigest = new GenericBatchDigest(digest);
|
this(DEFAULT_SERVER_THREADPOOL_SIZE, DEFAULT_FAIL_DELAY, DEFAULT_SUBSCRIPTION_INTERVAL);
|
||||||
|
}
|
||||||
minAbsoluteRedundancy = (int) (clientParams.getMinRedundancy() * (float) clientParams.getBulletinBoardAddressCount());
|
|
||||||
|
/**
|
||||||
executorService = Executors.newFixedThreadPool(JOBS_THREAD_NUM);
|
* Stores database locations and initializes the web Client
|
||||||
|
* Stores the required minimum redundancy.
|
||||||
clients = new ArrayList<>(clientParams.getBulletinBoardAddressCount());
|
* Starts the Thread Pool.
|
||||||
for (String address : clientParams.getBulletinBoardAddressList()){
|
* @param clientParams contains the required information
|
||||||
|
*/
|
||||||
SingleServerBulletinBoardClient client =
|
@Override
|
||||||
new SingleServerBulletinBoardClient(SERVER_THREADPOOL_SIZE, FAIL_DELAY, SUBSCRIPTION_INTERVAL);
|
public void init(BulletinBoardClientParams clientParams) {
|
||||||
|
|
||||||
client.init(BulletinBoardClientParams.newBuilder()
|
super.init(clientParams);
|
||||||
.addBulletinBoardAddress(address)
|
|
||||||
.build());
|
batchDigest = new GenericBulletinBoardDigest(digest);
|
||||||
|
|
||||||
clients.add(client);
|
minAbsoluteRedundancy = (int) (clientParams.getMinRedundancy() * (float) clientParams.getBulletinBoardAddressCount());
|
||||||
|
|
||||||
}
|
executorService = Executors.newFixedThreadPool(JOBS_THREAD_NUM);
|
||||||
|
|
||||||
}
|
clients = new ArrayList<>(clientParams.getBulletinBoardAddressCount());
|
||||||
|
for (String address : clientParams.getBulletinBoardAddressList()){
|
||||||
/**
|
|
||||||
* Post message to all DBs
|
SingleServerBulletinBoardClient client =
|
||||||
* Retry failed DBs
|
new SingleServerBulletinBoardClient(SERVER_THREADPOOL_SIZE, FAIL_DELAY, SUBSCRIPTION_INTERVAL);
|
||||||
* @param msg is the message,
|
|
||||||
* @return the message ID for later retrieval
|
client.init(BulletinBoardClientParams.newBuilder()
|
||||||
*/
|
.addBulletinBoardAddress(address)
|
||||||
@Override
|
.build());
|
||||||
public MessageID postMessage(BulletinBoardMessage msg, FutureCallback<Boolean> callback){
|
|
||||||
|
clients.add(client);
|
||||||
// Create job
|
|
||||||
MultiServerPostMessageWorker worker =
|
}
|
||||||
new MultiServerPostMessageWorker(clients, minAbsoluteRedundancy, msg, POST_MESSAGE_RETRY_NUM, callback);
|
|
||||||
|
}
|
||||||
// Submit job
|
|
||||||
executorService.submit(worker);
|
/**
|
||||||
|
* Post message to all DBs
|
||||||
// Calculate the correct message ID and return it
|
* Retry failed DBs
|
||||||
batchDigest.reset();
|
* @param msg is the message,
|
||||||
batchDigest.update(msg.getMsg());
|
* @return the message ID for later retrieval
|
||||||
return batchDigest.digestAsMessageID();
|
*/
|
||||||
|
@Override
|
||||||
}
|
public MessageID postMessage(BulletinBoardMessage msg, FutureCallback<Boolean> callback){
|
||||||
|
|
||||||
@Override
|
// Create job
|
||||||
public MessageID postBatch(CompleteBatch completeBatch, FutureCallback<Boolean> callback) {
|
MultiServerPostMessageWorker worker =
|
||||||
|
new MultiServerPostMessageWorker(clients, minAbsoluteRedundancy, msg, POST_MESSAGE_RETRY_NUM, callback);
|
||||||
// Create job
|
|
||||||
MultiServerPostBatchWorker worker =
|
// Submit job
|
||||||
new MultiServerPostBatchWorker(clients, minAbsoluteRedundancy, completeBatch, POST_MESSAGE_RETRY_NUM, callback);
|
executorService.submit(worker);
|
||||||
|
|
||||||
// Submit job
|
// Calculate the correct message ID and return it
|
||||||
executorService.submit(worker);
|
batchDigest.reset();
|
||||||
|
batchDigest.update(msg.getMsg());
|
||||||
// Calculate the correct message ID and return it
|
return batchDigest.digestAsMessageID();
|
||||||
batchDigest.reset();
|
|
||||||
batchDigest.update(completeBatch);
|
}
|
||||||
return batchDigest.digestAsMessageID();
|
|
||||||
|
@Override
|
||||||
}
|
public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize, FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
@Override
|
// Create job
|
||||||
public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback<Boolean> callback) {
|
MultiServerPostBatchWorker worker =
|
||||||
|
new MultiServerPostBatchWorker(clients, minAbsoluteRedundancy, msg, chunkSize, POST_MESSAGE_RETRY_NUM, callback);
|
||||||
// Create job
|
|
||||||
MultiServerBeginBatchWorker worker =
|
// Submit job
|
||||||
new MultiServerBeginBatchWorker(clients, minAbsoluteRedundancy, beginBatchMessage, POST_MESSAGE_RETRY_NUM, callback);
|
executorService.submit(worker);
|
||||||
|
|
||||||
// Submit job
|
// Calculate the correct message ID and return it
|
||||||
executorService.submit(worker);
|
batchDigest.reset();
|
||||||
|
batchDigest.update(msg);
|
||||||
}
|
return batchDigest.digestAsMessageID();
|
||||||
|
|
||||||
@Override
|
}
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList,
|
|
||||||
int startPosition, FutureCallback<Boolean> callback) {
|
@Override
|
||||||
|
public void beginBatch(Iterable<String> tags, FutureCallback<BatchIdentifier> callback) {
|
||||||
BatchDataContainer batchDataContainer = new BatchDataContainer(signerId, batchId, batchDataList, startPosition);
|
|
||||||
|
// Create job
|
||||||
// Create job
|
MultiServerBeginBatchWorker worker =
|
||||||
MultiServerPostBatchDataWorker worker =
|
new MultiServerBeginBatchWorker(clients, minAbsoluteRedundancy, tags, POST_MESSAGE_RETRY_NUM, callback);
|
||||||
new MultiServerPostBatchDataWorker(clients, minAbsoluteRedundancy, batchDataContainer, POST_MESSAGE_RETRY_NUM, callback);
|
|
||||||
|
// Submit job
|
||||||
// Submit job
|
executorService.submit(worker);
|
||||||
executorService.submit(worker);
|
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public void postBatchData(BatchIdentifier batchIdentifier, List<BatchChunk> batchChunkList,
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
int startPosition, FutureCallback<Boolean> callback) throws IllegalArgumentException {
|
||||||
|
|
||||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
// Cast identifier to usable form
|
||||||
|
|
||||||
}
|
if (!(batchIdentifier instanceof MultiServerBatchIdentifier)){
|
||||||
|
throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class.");
|
||||||
@Override
|
}
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList,
|
|
||||||
int startPosition, FutureCallback<Boolean> callback) {
|
MultiServerBatchIdentifier identifier = (MultiServerBatchIdentifier) batchIdentifier;
|
||||||
|
|
||||||
postBatchData(signerId.toByteArray(), batchId, batchDataList, startPosition, callback);
|
BatchDataContainer batchDataContainer = new BatchDataContainer(identifier, batchChunkList, startPosition);
|
||||||
|
|
||||||
}
|
// Create job
|
||||||
|
MultiServerPostBatchDataWorker worker =
|
||||||
@Override
|
new MultiServerPostBatchDataWorker(clients, minAbsoluteRedundancy, batchDataContainer, POST_MESSAGE_RETRY_NUM, callback);
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
|
||||||
|
// Submit job
|
||||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
executorService.submit(worker);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback<Boolean> callback) {
|
public void postBatchData(BatchIdentifier batchIdentifier, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback)
|
||||||
|
throws IllegalArgumentException {
|
||||||
// Create job
|
|
||||||
MultiServerCloseBatchWorker worker =
|
postBatchData(batchIdentifier, batchChunkList, 0, callback);
|
||||||
new MultiServerCloseBatchWorker(clients, minAbsoluteRedundancy, closeBatchMessage, POST_MESSAGE_RETRY_NUM, callback);
|
|
||||||
|
}
|
||||||
// Submit job
|
|
||||||
executorService.submit(worker);
|
@Override
|
||||||
|
public void closeBatch(BatchIdentifier payload, Timestamp timestamp, Iterable<Signature> signatures, FutureCallback<Boolean> callback)
|
||||||
}
|
throws IllegalArgumentException{
|
||||||
|
|
||||||
/**
|
if (!(payload instanceof MultiServerBatchIdentifier)) {
|
||||||
* Access each database and search for a given message ID
|
throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class.");
|
||||||
* Return the number of databases in which the message was found
|
}
|
||||||
* Only try once per DB
|
|
||||||
* Ignore communication exceptions in specific databases
|
MultiServerBatchIdentifier identifier = (MultiServerBatchIdentifier) payload;
|
||||||
*/
|
|
||||||
@Override
|
// Create job
|
||||||
public void getRedundancy(MessageID id, FutureCallback<Float> callback) {
|
MultiServerCloseBatchWorker worker =
|
||||||
|
new MultiServerCloseBatchWorker(clients, minAbsoluteRedundancy, identifier, timestamp, signatures, POST_MESSAGE_RETRY_NUM, callback);
|
||||||
// Create job
|
|
||||||
MultiServerGetRedundancyWorker worker =
|
// Submit job
|
||||||
new MultiServerGetRedundancyWorker(clients, minAbsoluteRedundancy, id, GET_REDUNDANCY_RETRY_NUM, callback);
|
executorService.submit(worker);
|
||||||
|
|
||||||
// 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
|
||||||
* Go through the DBs and try to retrieve messages according to the specified filter
|
* Ignore communication exceptions in specific databases
|
||||||
* 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 getRedundancy(MessageID id, FutureCallback<Float> callback) {
|
||||||
@Override
|
|
||||||
public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
// Create job
|
||||||
|
MultiServerGetRedundancyWorker worker =
|
||||||
// Create job
|
new MultiServerGetRedundancyWorker(clients, minAbsoluteRedundancy, id, GET_REDUNDANCY_RETRY_NUM, callback);
|
||||||
MultiServerReadMessagesWorker worker =
|
|
||||||
new MultiServerReadMessagesWorker(clients, minAbsoluteRedundancy, filterList, READ_MESSAGES_RETRY_NUM, callback);
|
// Submit job
|
||||||
|
executorService.submit(worker);
|
||||||
// Submit job
|
|
||||||
executorService.submit(worker);
|
}
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* Go through the DBs and try to retrieve messages according to the specified filter
|
||||||
@Override
|
* If at the operation is successful for some DB: return the results and stop iterating
|
||||||
public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback<CompleteBatch> callback) {
|
* If no operation is successful: return null (NOT blank list)
|
||||||
|
*/
|
||||||
// Create job
|
@Override
|
||||||
MultiServerReadBatchWorker worker =
|
public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||||
new MultiServerReadBatchWorker(clients, minAbsoluteRedundancy, batchSpecificationMessage, READ_MESSAGES_RETRY_NUM, callback);
|
|
||||||
|
// Create job
|
||||||
// Submit job
|
MultiServerReadMessagesWorker worker =
|
||||||
executorService.submit(worker);
|
new MultiServerReadMessagesWorker(clients, minAbsoluteRedundancy, filterList, 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
|
||||||
@Override
|
public void readMessage(MessageID msgID, FutureCallback<BulletinBoardMessage> callback) {
|
||||||
public void querySync(SyncQuery syncQuery, FutureCallback<SyncQueryResponse> callback) {
|
|
||||||
callback.onFailure(new IllegalAccessError("querySync is not supported by this class"));
|
//Create job
|
||||||
}
|
MultiServerReadMessageWorker worker =
|
||||||
|
new MultiServerReadMessageWorker(clients, minAbsoluteRedundancy, msgID, READ_MESSAGES_RETRY_NUM, callback);
|
||||||
@Override
|
|
||||||
public void close() {
|
// Submit job
|
||||||
super.close();
|
executorService.submit(worker);
|
||||||
|
|
||||||
try {
|
}
|
||||||
|
|
||||||
for (SingleServerBulletinBoardClient client : clients){
|
@Override
|
||||||
client.close();
|
public void readBatchData(BulletinBoardMessage stub, FutureCallback<BulletinBoardMessage> callback) throws IllegalArgumentException {
|
||||||
}
|
|
||||||
|
if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID) {
|
||||||
executorService.shutdown();
|
throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID");
|
||||||
while (! executorService.isShutdown()) {
|
}
|
||||||
executorService.awaitTermination(10, TimeUnit.SECONDS);
|
|
||||||
}
|
// Create job
|
||||||
} catch (InterruptedException e) {
|
MultiServerReadBatchDataWorker worker =
|
||||||
System.err.println(e.getCause() + " " + e.getMessage());
|
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<SyncQueryResponse> 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import meerkat.util.BulletinBoardUtils;
|
||||||
|
|
||||||
import static meerkat.protobuf.BulletinBoardAPI.FilterType.*;
|
import static meerkat.protobuf.BulletinBoardAPI.FilterType.*;
|
||||||
|
|
||||||
import java.sql.Time;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
@ -19,20 +18,22 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
*/
|
*/
|
||||||
public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber {
|
public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber {
|
||||||
|
|
||||||
protected final Collection<SubscriptionAsyncBulletinBoardClient> clients;
|
protected final Collection<SubscriptionBulletinBoardClient> clients;
|
||||||
protected final BulletinBoardClient localClient;
|
protected final BulletinBoardClient localClient;
|
||||||
|
|
||||||
protected Iterator<SubscriptionAsyncBulletinBoardClient> clientIterator;
|
protected Iterator<SubscriptionBulletinBoardClient> clientIterator;
|
||||||
protected SubscriptionAsyncBulletinBoardClient currentClient;
|
protected SubscriptionBulletinBoardClient currentClient;
|
||||||
|
|
||||||
private long lastServerSwitchTime;
|
private long lastServerSwitchTime;
|
||||||
|
|
||||||
private AtomicBoolean isSyncInProgress;
|
private AtomicBoolean isSyncInProgress;
|
||||||
private Semaphore rescheduleSemaphore;
|
private Semaphore rescheduleSemaphore;
|
||||||
|
|
||||||
|
private AtomicBoolean stopped;
|
||||||
|
|
||||||
private static final Float[] BREAKPOINTS = {0.5f, 0.75f, 0.9f, 0.95f, 0.99f, 0.999f};
|
private static final Float[] BREAKPOINTS = {0.5f, 0.75f, 0.9f, 0.95f, 0.99f, 0.999f};
|
||||||
|
|
||||||
public ThreadedBulletinBoardSubscriber(Collection<SubscriptionAsyncBulletinBoardClient> clients, BulletinBoardClient localClient) {
|
public ThreadedBulletinBoardSubscriber(Collection<SubscriptionBulletinBoardClient> clients, BulletinBoardClient localClient) {
|
||||||
|
|
||||||
this.clients = clients;
|
this.clients = clients;
|
||||||
this.localClient = localClient;
|
this.localClient = localClient;
|
||||||
|
@ -45,6 +46,8 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber
|
||||||
isSyncInProgress = new AtomicBoolean(false);
|
isSyncInProgress = new AtomicBoolean(false);
|
||||||
rescheduleSemaphore = new Semaphore(1);
|
rescheduleSemaphore = new Semaphore(1);
|
||||||
|
|
||||||
|
stopped = new AtomicBoolean(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,7 +135,8 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber
|
||||||
|
|
||||||
//TODO: log
|
//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<BulletinBoardMessage> result) {
|
public void onSuccess(List<BulletinBoardMessage> result) {
|
||||||
|
|
||||||
// Propagate result to caller
|
// Propagate result to caller
|
||||||
callback.onSuccess(result);
|
if (callback != null)
|
||||||
|
callback.onSuccess(result);
|
||||||
|
|
||||||
// Renew subscription
|
// Renew subscription
|
||||||
|
|
||||||
|
@ -245,12 +250,12 @@ public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber
|
||||||
super(filterList, callback);
|
super(filterList, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<BulletinBoardMessage> result) {
|
public void onSuccess(List<BulletinBoardMessage> result) {
|
||||||
|
|
||||||
// Propagate result to caller
|
// 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);
|
subscribe(filterList, 0, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,96 @@
|
||||||
package meerkat.bulletinboard.workers.multiserver;
|
package meerkat.bulletinboard.workers.multiserver;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
import meerkat.bulletinboard.MultiServerBatchIdentifier;
|
||||||
|
import meerkat.bulletinboard.MultiServerWorker;
|
||||||
import meerkat.bulletinboard.SingleServerBulletinBoardClient;
|
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.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||||
*/
|
*/
|
||||||
public class MultiServerBeginBatchWorker extends MultiServerGenericPostWorker<BeginBatchMessage> {
|
public class MultiServerBeginBatchWorker extends MultiServerWorker<Iterable<String>, BatchIdentifier> {
|
||||||
|
|
||||||
|
private BatchIdentifier[] identifiers;
|
||||||
|
private AtomicInteger remainingServers;
|
||||||
|
|
||||||
public MultiServerBeginBatchWorker(List<SingleServerBulletinBoardClient> clients,
|
public MultiServerBeginBatchWorker(List<SingleServerBulletinBoardClient> clients,
|
||||||
int minServers, BeginBatchMessage payload, int maxRetry,
|
int minServers, Iterable<String> payload, int maxRetry,
|
||||||
FutureCallback<Boolean> futureCallback) {
|
FutureCallback<BatchIdentifier> futureCallback) {
|
||||||
|
|
||||||
super(clients, minServers, payload, maxRetry, 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<BatchIdentifier> {
|
||||||
|
|
||||||
|
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
|
@Override
|
||||||
protected void doPost(SingleServerBulletinBoardClient client, BeginBatchMessage payload) {
|
public void onSuccess(BatchIdentifier result) {
|
||||||
client.beginBatch(payload, this);
|
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++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,83 @@
|
||||||
package meerkat.bulletinboard.workers.multiserver;
|
package meerkat.bulletinboard.workers.multiserver;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
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.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;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||||
*/
|
*/
|
||||||
public class MultiServerCloseBatchWorker extends MultiServerGenericPostWorker<CloseBatchMessage> {
|
public class MultiServerCloseBatchWorker extends MultiServerWorker<MultiServerBatchIdentifier, Boolean> {
|
||||||
|
|
||||||
|
private final Timestamp timestamp;
|
||||||
|
private final Iterable<Crypto.Signature> signatures;
|
||||||
|
|
||||||
public MultiServerCloseBatchWorker(List<SingleServerBulletinBoardClient> clients,
|
public MultiServerCloseBatchWorker(List<SingleServerBulletinBoardClient> clients,
|
||||||
int minServers, CloseBatchMessage payload, int maxRetry,
|
int minServers, MultiServerBatchIdentifier payload, Timestamp timestamp, Iterable<Crypto.Signature> signatures,
|
||||||
FutureCallback<Boolean> futureCallback) {
|
int maxRetry, FutureCallback<Boolean> futureCallback) {
|
||||||
|
|
||||||
super(clients, minServers, payload, maxRetry, futureCallback);
|
super(clients, minServers, payload, maxRetry, futureCallback);
|
||||||
|
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.signatures = signatures;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(SingleServerBulletinBoardClient client, CloseBatchMessage payload) {
|
public void run() {
|
||||||
client.closeBatch(payload, this);
|
|
||||||
|
Iterator<BatchIdentifier> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,14 +35,9 @@ public abstract class MultiServerGenericPostWorker<T> extends MultiServerWorker<
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
// Iterate through servers
|
// Iterate through servers
|
||||||
|
for (SingleServerBulletinBoardClient client : clients) {
|
||||||
Iterator<SingleServerBulletinBoardClient> clientIterator = getClientIterator();
|
|
||||||
|
|
||||||
while (clientIterator.hasNext()) {
|
|
||||||
|
|
||||||
// Send request to Server
|
// Send request to Server
|
||||||
SingleServerBulletinBoardClient client = clientIterator.next();
|
|
||||||
|
|
||||||
doPost(client, payload);
|
doPost(client, payload);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,9 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public abstract class MultiServerGenericReadWorker<IN, OUT> extends MultiServerWorker<IN, OUT>{
|
public abstract class MultiServerGenericReadWorker<IN, OUT> extends MultiServerWorker<IN, OUT>{
|
||||||
|
|
||||||
private final Iterator<SingleServerBulletinBoardClient> clientIterator;
|
private Iterator<SingleServerBulletinBoardClient> clientIterator;
|
||||||
|
|
||||||
|
private String errorString;
|
||||||
|
|
||||||
public MultiServerGenericReadWorker(List<SingleServerBulletinBoardClient> clients,
|
public MultiServerGenericReadWorker(List<SingleServerBulletinBoardClient> clients,
|
||||||
int minServers, IN payload, int maxRetry,
|
int minServers, IN payload, int maxRetry,
|
||||||
|
@ -22,7 +24,8 @@ public abstract class MultiServerGenericReadWorker<IN, OUT> extends MultiServerW
|
||||||
|
|
||||||
super(clients, true, minServers, payload, maxRetry, futureCallback); // Shuffle clients on creation to balance load
|
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<IN, OUT> extends MultiServerW
|
||||||
doRead(payload, client);
|
doRead(payload, client);
|
||||||
|
|
||||||
} else {
|
} 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<IN, OUT> extends MultiServerW
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Throwable t) {
|
public void onFailure(Throwable t) {
|
||||||
|
//TODO: log
|
||||||
|
errorString += t.getCause() + " " + t.getMessage() + "\n";
|
||||||
run(); // Retry with next server
|
run(); // Retry with next server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,13 +36,8 @@ public class MultiServerGetRedundancyWorker extends MultiServerWorker<MessageID,
|
||||||
*/
|
*/
|
||||||
public void run(){
|
public void run(){
|
||||||
|
|
||||||
Iterator<SingleServerBulletinBoardClient> clientIterator = getClientIterator();
|
|
||||||
|
|
||||||
// Iterate through clients
|
// Iterate through clients
|
||||||
|
for (SingleServerBulletinBoardClient client : clients) {
|
||||||
while (clientIterator.hasNext()) {
|
|
||||||
|
|
||||||
SingleServerBulletinBoardClient client = clientIterator.next();
|
|
||||||
|
|
||||||
// Send request to client
|
// Send request to client
|
||||||
client.getRedundancy(payload,this);
|
client.getRedundancy(payload,this);
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
package meerkat.bulletinboard.workers.multiserver;
|
package meerkat.bulletinboard.workers.multiserver;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier;
|
||||||
|
import meerkat.bulletinboard.MultiServerWorker;
|
||||||
import meerkat.bulletinboard.SingleServerBulletinBoardClient;
|
import meerkat.bulletinboard.SingleServerBulletinBoardClient;
|
||||||
import meerkat.bulletinboard.BatchDataContainer;
|
import meerkat.bulletinboard.BatchDataContainer;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||||
*/
|
*/
|
||||||
public class MultiServerPostBatchDataWorker extends MultiServerGenericPostWorker<BatchDataContainer> {
|
public class MultiServerPostBatchDataWorker extends MultiServerWorker<BatchDataContainer, Boolean> {
|
||||||
|
|
||||||
public MultiServerPostBatchDataWorker(List<SingleServerBulletinBoardClient> clients,
|
public MultiServerPostBatchDataWorker(List<SingleServerBulletinBoardClient> clients,
|
||||||
int minServers, BatchDataContainer payload, int maxRetry,
|
int minServers, BatchDataContainer payload, int maxRetry,
|
||||||
|
@ -20,9 +23,50 @@ public class MultiServerPostBatchDataWorker extends MultiServerGenericPostWorker
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(SingleServerBulletinBoardClient client, BatchDataContainer payload) {
|
public void run() {
|
||||||
client.postBatchData(payload.signerId, payload.batchId, payload.batchDataList, payload.startPosition, this);
|
|
||||||
|
Iterator<BatchIdentifier> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,33 @@
|
||||||
package meerkat.bulletinboard.workers.multiserver;
|
package meerkat.bulletinboard.workers.multiserver;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import meerkat.bulletinboard.CompleteBatch;
|
|
||||||
import meerkat.bulletinboard.SingleServerBulletinBoardClient;
|
import meerkat.bulletinboard.SingleServerBulletinBoardClient;
|
||||||
|
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||||
*/
|
*/
|
||||||
public class MultiServerPostBatchWorker extends MultiServerGenericPostWorker<CompleteBatch> {
|
public class MultiServerPostBatchWorker extends MultiServerGenericPostWorker<BulletinBoardMessage> {
|
||||||
|
|
||||||
|
private final int chunkSize;
|
||||||
|
|
||||||
public MultiServerPostBatchWorker(List<SingleServerBulletinBoardClient> clients,
|
public MultiServerPostBatchWorker(List<SingleServerBulletinBoardClient> clients,
|
||||||
int minServers, CompleteBatch payload, int maxRetry,
|
int minServers, BulletinBoardMessage payload, int chunkSize, int maxRetry,
|
||||||
FutureCallback<Boolean> futureCallback) {
|
FutureCallback<Boolean> futureCallback) {
|
||||||
|
|
||||||
super(clients, minServers, payload, maxRetry, futureCallback);
|
super(clients, minServers, payload, maxRetry, futureCallback);
|
||||||
|
|
||||||
|
this.chunkSize = chunkSize;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(SingleServerBulletinBoardClient client, CompleteBatch payload) {
|
protected void doPost(SingleServerBulletinBoardClient client, BulletinBoardMessage payload) {
|
||||||
client.postBatch(payload, this);
|
|
||||||
|
client.postAsBatch(payload, chunkSize, this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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<BulletinBoardMessage, BulletinBoardMessage> {
|
||||||
|
|
||||||
|
public MultiServerReadBatchDataWorker(List<SingleServerBulletinBoardClient> clients,
|
||||||
|
int minServers, BulletinBoardMessage payload, int maxRetry,
|
||||||
|
FutureCallback<BulletinBoardMessage> futureCallback) {
|
||||||
|
|
||||||
|
super(clients, minServers, payload, maxRetry, futureCallback);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doRead(BulletinBoardMessage payload, SingleServerBulletinBoardClient client) {
|
||||||
|
client.readBatchData(payload, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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<BatchSpecificationMessage, CompleteBatch> {
|
|
||||||
|
|
||||||
public MultiServerReadBatchWorker(List<SingleServerBulletinBoardClient> clients,
|
|
||||||
int minServers, BatchSpecificationMessage payload, int maxRetry,
|
|
||||||
FutureCallback<CompleteBatch> futureCallback) {
|
|
||||||
|
|
||||||
super(clients, minServers, payload, maxRetry, futureCallback);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doRead(BatchSpecificationMessage payload, SingleServerBulletinBoardClient client) {
|
|
||||||
client.readBatch(payload, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<MessageID, BulletinBoardMessage> {
|
||||||
|
|
||||||
|
public MultiServerReadMessageWorker(List<SingleServerBulletinBoardClient> clients,
|
||||||
|
int minServers, MessageID payload, int maxRetry,
|
||||||
|
FutureCallback<BulletinBoardMessage> futureCallback) {
|
||||||
|
|
||||||
|
super(clients, minServers, payload, maxRetry, futureCallback);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doRead(MessageID payload, SingleServerBulletinBoardClient client) {
|
||||||
|
client.readMessage(payload, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,17 +1,53 @@
|
||||||
package meerkat.bulletinboard.workers.singleserver;
|
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.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.BEGIN_BATCH_PATH;
|
||||||
|
import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||||
* Tries to contact server once and perform a post operation
|
* Tries to contact server once and perform a post operation
|
||||||
*/
|
*/
|
||||||
public class SingleServerBeginBatchWorker extends SingleServerGenericPostWorker<BeginBatchMessage> {
|
public class SingleServerBeginBatchWorker extends SingleServerWorker<BeginBatchMessage,Int64Value> {
|
||||||
|
|
||||||
public SingleServerBeginBatchWorker(String serverAddress, BeginBatchMessage payload, int maxRetry) {
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<GenerateSyncQueryParams,SyncQuery> {
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,6 @@ import meerkat.comm.MessageInputStream;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.rest.Constants;
|
import meerkat.rest.Constants;
|
||||||
|
|
||||||
import javax.ws.rs.ProcessingException;
|
|
||||||
import javax.ws.rs.client.Client;
|
import javax.ws.rs.client.Client;
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Entity;
|
||||||
import javax.ws.rs.client.WebTarget;
|
import javax.ws.rs.client.WebTarget;
|
||||||
|
@ -14,7 +13,6 @@ import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
|
|
||||||
import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH;
|
import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH;
|
||||||
import static meerkat.bulletinboard.BulletinBoardConstants.READ_MESSAGES_PATH;
|
import static meerkat.bulletinboard.BulletinBoardConstants.READ_MESSAGES_PATH;
|
||||||
|
|
|
@ -1,35 +1,28 @@
|
||||||
package meerkat.bulletinboard.workers.singleserver;
|
package meerkat.bulletinboard.workers.singleserver;
|
||||||
|
|
||||||
import meerkat.bulletinboard.CompleteBatch;
|
|
||||||
import meerkat.bulletinboard.SingleServerWorker;
|
import meerkat.bulletinboard.SingleServerWorker;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.comm.MessageInputStream;
|
import meerkat.comm.MessageInputStream;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.rest.Constants;
|
import meerkat.rest.Constants;
|
||||||
|
|
||||||
import javax.ws.rs.ProcessingException;
|
|
||||||
import javax.ws.rs.client.Client;
|
import javax.ws.rs.client.Client;
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Entity;
|
||||||
import javax.ws.rs.client.WebTarget;
|
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.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH;
|
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.READ_BATCH_PATH;
|
||||||
|
|
||||||
import static meerkat.bulletinboard.BulletinBoardConstants.BATCH_ID_TAG_PREFIX;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||||
*/
|
*/
|
||||||
public class SingleServerReadBatchWorker extends SingleServerWorker<BatchSpecificationMessage, List<BatchData>> {
|
public class SingleServerReadBatchWorker extends SingleServerWorker<BatchQuery, List<BatchChunk>> {
|
||||||
|
|
||||||
public SingleServerReadBatchWorker(String serverAddress, BatchSpecificationMessage payload, int maxRetry) {
|
public SingleServerReadBatchWorker(String serverAddress, BatchQuery payload, int maxRetry) {
|
||||||
super(serverAddress, payload, maxRetry);
|
super(serverAddress, payload, maxRetry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +32,7 @@ public class SingleServerReadBatchWorker extends SingleServerWorker<BatchSpecifi
|
||||||
* @return the complete batch as read from the server
|
* @return the complete batch as read from the server
|
||||||
* @throws CommunicationException if the server's response is invalid
|
* @throws CommunicationException if the server's response is invalid
|
||||||
*/
|
*/
|
||||||
public List<BatchData> call() throws CommunicationException{
|
public List<BatchChunk> call() throws CommunicationException{
|
||||||
|
|
||||||
Client client = clientLocal.get();
|
Client client = clientLocal.get();
|
||||||
|
|
||||||
|
@ -50,11 +43,11 @@ public class SingleServerReadBatchWorker extends SingleServerWorker<BatchSpecifi
|
||||||
webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(READ_BATCH_PATH);
|
webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(READ_BATCH_PATH);
|
||||||
InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF), InputStream.class);
|
InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF), InputStream.class);
|
||||||
|
|
||||||
MessageInputStream<BatchData> inputStream = null;
|
MessageInputStream<BatchChunk> inputStream = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
inputStream = MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BatchData.class);
|
inputStream = MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BatchChunk.class);
|
||||||
|
|
||||||
return inputStream.asList();
|
return inputStream.asList();
|
||||||
|
|
||||||
|
|
|
@ -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<Throwable> 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<SyncStatus> {
|
||||||
|
|
||||||
|
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<Integer> {
|
||||||
|
|
||||||
|
private int[] expectedCounts;
|
||||||
|
private int currentIteration;
|
||||||
|
|
||||||
|
public MessageCountCallback(int[] expectedCounts) {
|
||||||
|
this.expectedCounts = expectedCounts;
|
||||||
|
this.currentIteration = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Integer result) {
|
||||||
|
|
||||||
|
if (currentIteration < expectedCounts.length){
|
||||||
|
if (result != expectedCounts[currentIteration]){
|
||||||
|
onFailure(new AssertionError("Wrong message count. Expected " + expectedCounts[currentIteration] + " but received " + result));
|
||||||
|
currentIteration = expectedCounts.length;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentIteration++;
|
||||||
|
|
||||||
|
if (currentIteration == expectedCounts.length)
|
||||||
|
semaphore.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable t) {
|
||||||
|
thrown.add(t);
|
||||||
|
semaphore.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSync() throws SignatureException, CommunicationException, InterruptedException {
|
||||||
|
|
||||||
|
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<BulletinBoardMessage> msgList = remoteClient.readMessages(MessageFilterList.newBuilder()
|
||||||
|
.addFilter(MessageFilter.newBuilder()
|
||||||
|
.setType(FilterType.MSG_ID)
|
||||||
|
.setId(msgID.getID())
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
assertThat("Wrong number of messages returned.", msgList.size() == 1);
|
||||||
|
assertThat("Returned message is not equal to original one", messageComparator.compare(msgList.get(0),msg) == 0);
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<SubscriptionBulletinBoardClient> 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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,10 +5,13 @@ import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.Timestamp;
|
import com.google.protobuf.Timestamp;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.crypto.concrete.ECDSASignature;
|
import meerkat.crypto.concrete.ECDSASignature;
|
||||||
|
import meerkat.crypto.concrete.SHA256Digest;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.protobuf.Crypto;
|
import meerkat.protobuf.Crypto;
|
||||||
import meerkat.util.BulletinBoardMessageComparator;
|
import meerkat.util.BulletinBoardMessageComparator;
|
||||||
import meerkat.util.BulletinBoardMessageGenerator;
|
import meerkat.util.BulletinBoardMessageGenerator;
|
||||||
|
import meerkat.util.BulletinBoardUtils;
|
||||||
|
import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -28,7 +31,7 @@ public class GenericBulletinBoardClientTester {
|
||||||
|
|
||||||
// Signature resources
|
// Signature resources
|
||||||
|
|
||||||
private GenericBatchDigitalSignature signers[];
|
private BulletinBoardSignature signers[];
|
||||||
private ByteString[] signerIDs;
|
private ByteString[] signerIDs;
|
||||||
|
|
||||||
private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
|
private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
|
||||||
|
@ -45,28 +48,29 @@ public class GenericBulletinBoardClientTester {
|
||||||
private AsyncBulletinBoardClient bulletinBoardClient;
|
private AsyncBulletinBoardClient bulletinBoardClient;
|
||||||
|
|
||||||
private PostCallback postCallback;
|
private PostCallback postCallback;
|
||||||
private PostCallback failPostCallback = new PostCallback(true,false);
|
|
||||||
|
|
||||||
private RedundancyCallback redundancyCallback;
|
private RedundancyCallback redundancyCallback;
|
||||||
private ReadCallback readCallback;
|
private ReadCallback readCallback;
|
||||||
private ReadBatchCallback readBatchCallback;
|
|
||||||
|
|
||||||
// Sync and misc
|
// Sync and misc
|
||||||
|
|
||||||
private Semaphore jobSemaphore;
|
private Semaphore jobSemaphore;
|
||||||
private Vector<Throwable> thrown;
|
private Vector<Throwable> thrown;
|
||||||
private Random random;
|
private Random random;
|
||||||
|
private BulletinBoardMessageGenerator generator;
|
||||||
|
|
||||||
|
private BulletinBoardDigest digest;
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
|
|
||||||
public GenericBulletinBoardClientTester(AsyncBulletinBoardClient bulletinBoardClient){
|
public GenericBulletinBoardClientTester(AsyncBulletinBoardClient bulletinBoardClient, int seed){
|
||||||
|
|
||||||
this.bulletinBoardClient = bulletinBoardClient;
|
this.bulletinBoardClient = bulletinBoardClient;
|
||||||
|
|
||||||
signers = new GenericBatchDigitalSignature[2];
|
signers = new GenericBulletinBoardSignature[2];
|
||||||
signerIDs = new ByteString[signers.length];
|
signerIDs = new ByteString[signers.length];
|
||||||
signers[0] = new GenericBatchDigitalSignature(new ECDSASignature());
|
signers[0] = new GenericBulletinBoardSignature(new ECDSASignature());
|
||||||
signers[1] = new GenericBatchDigitalSignature(new ECDSASignature());
|
signers[1] = new GenericBulletinBoardSignature(new ECDSASignature());
|
||||||
|
|
||||||
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
||||||
char[] password = KEYFILE_PASSWORD1.toCharArray();
|
char[] password = KEYFILE_PASSWORD1.toCharArray();
|
||||||
|
@ -108,6 +112,10 @@ public class GenericBulletinBoardClientTester {
|
||||||
fail("Couldn't find signing key " + e.getMessage());
|
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
|
// Callback definitions
|
||||||
|
@ -138,16 +146,21 @@ public class GenericBulletinBoardClientTester {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Boolean msg) {
|
public void onSuccess(Boolean msg) {
|
||||||
|
|
||||||
System.err.println("Post operation completed");
|
System.err.println("Post operation completed");
|
||||||
jobSemaphore.release();
|
|
||||||
//TODO: Change Assert mechanism to exception one
|
|
||||||
if (isAssert) {
|
if (isAssert) {
|
||||||
if (assertValue) {
|
if (assertValue && !msg) {
|
||||||
assertThat("Post operation failed", msg, is(Boolean.TRUE));
|
genericHandleFailure(new AssertionError("Post operation failed"));
|
||||||
|
} else if (!assertValue && msg){
|
||||||
|
genericHandleFailure(new AssertionError("Post operation succeeded unexpectedly"));
|
||||||
} else {
|
} else {
|
||||||
assertThat("Post operation succeeded unexpectedly", msg, is(Boolean.FALSE));
|
jobSemaphore.release();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
jobSemaphore.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -210,21 +223,24 @@ public class GenericBulletinBoardClientTester {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ReadBatchCallback implements FutureCallback<CompleteBatch> {
|
private class ReadBatchCallback implements FutureCallback<BulletinBoardMessage>{
|
||||||
|
|
||||||
private CompleteBatch expectedBatch;
|
private BulletinBoardMessage expectedMsg;
|
||||||
|
|
||||||
public ReadBatchCallback(CompleteBatch expectedBatch) {
|
public ReadBatchCallback(BulletinBoardMessage expectedMsg) {
|
||||||
this.expectedBatch = expectedBatch;
|
this.expectedMsg = expectedMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(CompleteBatch batch) {
|
public void onSuccess(BulletinBoardMessage msg) {
|
||||||
|
|
||||||
System.err.println(batch);
|
BulletinBoardMessageComparator msgComparator = new BulletinBoardMessageComparator();
|
||||||
jobSemaphore.release();
|
|
||||||
|
|
||||||
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
|
// Test methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -311,7 +274,13 @@ public class GenericBulletinBoardClientTester {
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
||||||
if (thrown.size() > 0) {
|
if (thrown.size() > 0) {
|
||||||
|
|
||||||
|
for (Throwable t : thrown){
|
||||||
|
System.err.println(t.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
assert false;
|
assert false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -319,7 +288,7 @@ public class GenericBulletinBoardClientTester {
|
||||||
/**
|
/**
|
||||||
* Tests the standard post, redundancy and read methods
|
* 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[] b1 = {(byte) 1, (byte) 2, (byte) 3, (byte) 4};
|
||||||
byte[] b2 = {(byte) 11, (byte) 12, (byte) 13, (byte) 14};
|
byte[] b2 = {(byte) 11, (byte) 12, (byte) 13, (byte) 14};
|
||||||
|
@ -395,59 +364,69 @@ public class GenericBulletinBoardClientTester {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests posting a batch by parts
|
* Tests posting a batch by parts
|
||||||
* Also tests not being able to post to a closed batch
|
|
||||||
* @throws CommunicationException, SignatureException, InterruptedException
|
* @throws CommunicationException, SignatureException, InterruptedException
|
||||||
*/
|
*/
|
||||||
public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException {
|
public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException {
|
||||||
|
|
||||||
final int SIGNER = 1;
|
|
||||||
final int BATCH_ID = 100;
|
|
||||||
final int BATCH_LENGTH = 100;
|
final int BATCH_LENGTH = 100;
|
||||||
|
final int CHUNK_SIZE = 10;
|
||||||
|
final int TAG_NUM = 10;
|
||||||
|
|
||||||
CompleteBatch completeBatch = createRandomBatch(SIGNER, BATCH_ID, BATCH_LENGTH);
|
final Timestamp timestamp = Timestamp.newBuilder()
|
||||||
|
.setSeconds(141515)
|
||||||
|
.setNanos(859018)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final BulletinBoardMessage msg = generator.generateRandomMessage(signers, timestamp, BATCH_LENGTH, TAG_NUM);
|
||||||
|
|
||||||
// Begin batch
|
// Begin batch
|
||||||
|
|
||||||
bulletinBoardClient.beginBatch(completeBatch.getBeginBatchMessage(), postCallback);
|
bulletinBoardClient.beginBatch(msg.getMsg().getTagList(), new FutureCallback<BatchIdentifier>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final BatchIdentifier identifier) {
|
||||||
|
|
||||||
|
bulletinBoardClient.postBatchData(identifier, BulletinBoardUtils.breakToBatch(msg, CHUNK_SIZE), new FutureCallback<Boolean>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Boolean result) {
|
||||||
|
|
||||||
|
bulletinBoardClient.closeBatch(identifier, msg.getMsg().getTimestamp(), msg.getSigList(), new FutureCallback<Boolean>() {
|
||||||
|
@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();
|
jobSemaphore.acquire();
|
||||||
|
|
||||||
// Post data
|
digest.reset();
|
||||||
|
digest.update(msg);
|
||||||
|
|
||||||
bulletinBoardClient.postBatchData(signerIDs[SIGNER], BATCH_ID, completeBatch.getBatchDataList(), postCallback);
|
bulletinBoardClient.readMessage(digest.digestAsMessageID(), new ReadBatchCallback(msg));
|
||||||
|
|
||||||
jobSemaphore.acquire();
|
|
||||||
|
|
||||||
// Close batch
|
|
||||||
|
|
||||||
CloseBatchMessage closeBatchMessage = completeBatch.getCloseBatchMessage();
|
|
||||||
|
|
||||||
bulletinBoardClient.closeBatch(closeBatchMessage, postCallback);
|
|
||||||
|
|
||||||
jobSemaphore.acquire();
|
|
||||||
|
|
||||||
// Attempt to open batch again
|
|
||||||
|
|
||||||
bulletinBoardClient.beginBatch(completeBatch.getBeginBatchMessage(), failPostCallback);
|
|
||||||
|
|
||||||
// Attempt to add batch data
|
|
||||||
|
|
||||||
bulletinBoardClient.postBatchData(signerIDs[SIGNER], BATCH_ID, completeBatch.getBatchDataList(), failPostCallback);
|
|
||||||
|
|
||||||
jobSemaphore.acquire(2);
|
|
||||||
|
|
||||||
// Read batch data
|
|
||||||
|
|
||||||
BatchSpecificationMessage batchSpecificationMessage =
|
|
||||||
BatchSpecificationMessage.newBuilder()
|
|
||||||
.setSignerId(signerIDs[SIGNER])
|
|
||||||
.setBatchId(BATCH_ID)
|
|
||||||
.setStartPosition(0)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
readBatchCallback = new ReadBatchCallback(completeBatch);
|
|
||||||
|
|
||||||
bulletinBoardClient.readBatch(batchSpecificationMessage, readBatchCallback);
|
|
||||||
|
|
||||||
jobSemaphore.acquire();
|
jobSemaphore.acquire();
|
||||||
|
|
||||||
|
@ -455,62 +434,61 @@ public class GenericBulletinBoardClientTester {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Posts a complete batch message
|
* Posts a complete batch message
|
||||||
* Checks reading of the message
|
* Checks reading of the message in two parts
|
||||||
* @throws CommunicationException, SignatureException, InterruptedException
|
* @throws CommunicationException, SignatureException, InterruptedException
|
||||||
*/
|
*/
|
||||||
public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException {
|
public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException {
|
||||||
|
|
||||||
final int SIGNER = 0;
|
final int BATCH_LENGTH = 100;
|
||||||
final int BATCH_ID = 101;
|
final int CHUNK_SIZE = 99;
|
||||||
final int BATCH_LENGTH = 50;
|
final int TAG_NUM = 8;
|
||||||
|
|
||||||
|
final Timestamp timestamp = Timestamp.newBuilder()
|
||||||
|
.setSeconds(7776151)
|
||||||
|
.setNanos(252616)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final BulletinBoardMessage msg = generator.generateRandomMessage(signers, timestamp, BATCH_LENGTH, TAG_NUM);
|
||||||
|
|
||||||
// Post batch
|
// Post batch
|
||||||
|
|
||||||
CompleteBatch completeBatch = createRandomBatch(SIGNER, BATCH_ID, BATCH_LENGTH);
|
MessageID msgID = bulletinBoardClient.postAsBatch(msg, CHUNK_SIZE, postCallback);
|
||||||
|
|
||||||
bulletinBoardClient.postBatch(completeBatch,postCallback);
|
|
||||||
|
|
||||||
jobSemaphore.acquire();
|
jobSemaphore.acquire();
|
||||||
|
|
||||||
// Read batch
|
// Read batch
|
||||||
|
|
||||||
BatchSpecificationMessage batchSpecificationMessage =
|
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||||
BatchSpecificationMessage.newBuilder()
|
.addFilter(MessageFilter.newBuilder()
|
||||||
.setSignerId(signerIDs[SIGNER])
|
.setType(FilterType.MSG_ID)
|
||||||
.setBatchId(BATCH_ID)
|
.setId(msgID.getID())
|
||||||
.setStartPosition(0)
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
readBatchCallback = new ReadBatchCallback(completeBatch);
|
bulletinBoardClient.readMessages(filterList, new FutureCallback<List<BulletinBoardMessage>>() {
|
||||||
|
|
||||||
bulletinBoardClient.readBatch(batchSpecificationMessage, readBatchCallback);
|
@Override
|
||||||
|
public void onSuccess(List<BulletinBoardMessage> msgList) {
|
||||||
|
|
||||||
jobSemaphore.acquire();
|
if (msgList.size() != 1) {
|
||||||
|
|
||||||
}
|
genericHandleFailure(new AssertionError("Wrong number of stubs returned. Expected: 1; Found: " + msgList.size()));
|
||||||
|
|
||||||
/**
|
} else {
|
||||||
* Tests that an unopened batch cannot be closed
|
|
||||||
* @throws CommunicationException, InterruptedException
|
|
||||||
*/
|
|
||||||
public void testInvalidBatchClose() throws CommunicationException, InterruptedException {
|
|
||||||
|
|
||||||
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();
|
jobSemaphore.acquire();
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,6 @@ import java.security.cert.CertificateException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.Semaphore;
|
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;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,7 +23,7 @@ import static org.junit.Assert.fail;
|
||||||
*/
|
*/
|
||||||
public class GenericSubscriptionClientTester {
|
public class GenericSubscriptionClientTester {
|
||||||
|
|
||||||
private GenericBatchDigitalSignature signers[];
|
private BulletinBoardSignature signers[];
|
||||||
private ByteString[] signerIDs;
|
private ByteString[] signerIDs;
|
||||||
|
|
||||||
private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
|
private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
|
||||||
|
@ -38,7 +35,7 @@ public class GenericSubscriptionClientTester {
|
||||||
private static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt";
|
private static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt";
|
||||||
private static String CERT3_PEM_EXAMPLE = "/certs/enduser-certs/user3.crt";
|
private static String CERT3_PEM_EXAMPLE = "/certs/enduser-certs/user3.crt";
|
||||||
|
|
||||||
private SubscriptionAsyncBulletinBoardClient bulletinBoardClient;
|
private SubscriptionBulletinBoardClient bulletinBoardClient;
|
||||||
|
|
||||||
private Random random;
|
private Random random;
|
||||||
private BulletinBoardMessageGenerator generator;
|
private BulletinBoardMessageGenerator generator;
|
||||||
|
@ -46,14 +43,14 @@ public class GenericSubscriptionClientTester {
|
||||||
private Semaphore jobSemaphore;
|
private Semaphore jobSemaphore;
|
||||||
private Vector<Throwable> thrown;
|
private Vector<Throwable> thrown;
|
||||||
|
|
||||||
public GenericSubscriptionClientTester(SubscriptionAsyncBulletinBoardClient bulletinBoardClient){
|
public GenericSubscriptionClientTester(SubscriptionBulletinBoardClient bulletinBoardClient){
|
||||||
|
|
||||||
this.bulletinBoardClient = bulletinBoardClient;
|
this.bulletinBoardClient = bulletinBoardClient;
|
||||||
|
|
||||||
signers = new GenericBatchDigitalSignature[2];
|
signers = new BulletinBoardSignature[2];
|
||||||
signerIDs = new ByteString[signers.length];
|
signerIDs = new ByteString[signers.length];
|
||||||
signers[0] = new GenericBatchDigitalSignature(new ECDSASignature());
|
signers[0] = new GenericBulletinBoardSignature(new ECDSASignature());
|
||||||
signers[1] = new GenericBatchDigitalSignature(new ECDSASignature());
|
signers[1] = new GenericBulletinBoardSignature(new ECDSASignature());
|
||||||
|
|
||||||
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
||||||
char[] password = KEYFILE_PASSWORD1.toCharArray();
|
char[] password = KEYFILE_PASSWORD1.toCharArray();
|
||||||
|
@ -181,17 +178,15 @@ public class GenericSubscriptionClientTester {
|
||||||
public void onFailure(Throwable t) {
|
public void onFailure(Throwable t) {
|
||||||
System.err.println(t.getCause() + " " + t.getMessage());
|
System.err.println(t.getCause() + " " + t.getMessage());
|
||||||
thrown.add(t);
|
thrown.add(t);
|
||||||
jobSemaphore.release(expectedMessages.size());
|
jobSemaphore.release();
|
||||||
stage = expectedMessages.size();
|
stage = expectedMessages.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void subscriptionTest() throws SignatureException, CommunicationException {
|
public void subscriptionTest() throws SignatureException, CommunicationException {
|
||||||
|
|
||||||
final int FIRST_POST_ID = 201;
|
|
||||||
final int SECOND_POST_ID = 202;
|
|
||||||
final String COMMON_TAG = "SUBSCRIPTION_TEST";
|
final String COMMON_TAG = "SUBSCRIPTION_TEST";
|
||||||
|
|
||||||
List<String> tags = new LinkedList<>();
|
List<String> tags = new LinkedList<>();
|
||||||
tags.add(COMMON_TAG);
|
tags.add(COMMON_TAG);
|
||||||
|
|
||||||
|
@ -207,9 +202,9 @@ public class GenericSubscriptionClientTester {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
List<List<BulletinBoardMessage>> expectedMessages = new ArrayList<>(3);
|
List<List<BulletinBoardMessage>> expectedMessages = new ArrayList<>(3);
|
||||||
expectedMessages.add(new LinkedList<BulletinBoardMessage>());
|
expectedMessages.add(new LinkedList<>());
|
||||||
expectedMessages.add(new LinkedList<BulletinBoardMessage>());
|
expectedMessages.add(new LinkedList<>());
|
||||||
expectedMessages.add(new LinkedList<BulletinBoardMessage>());
|
expectedMessages.add(new LinkedList<>());
|
||||||
expectedMessages.get(0).add(msg1);
|
expectedMessages.get(0).add(msg1);
|
||||||
expectedMessages.get(2).add(msg3);
|
expectedMessages.get(2).add(msg3);
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,6 @@ import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.security.SignatureException;
|
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.
|
* Created by Arbel Deutsch Peled on 05-Dec-15.
|
||||||
|
@ -30,30 +24,14 @@ public class LocalBulletinBoardClientTest {
|
||||||
|
|
||||||
public LocalBulletinBoardClientTest() throws CommunicationException {
|
public LocalBulletinBoardClientTest() throws CommunicationException {
|
||||||
|
|
||||||
H2QueryProvider queryProvider = new H2QueryProvider(DB_NAME) ;
|
H2QueryProvider queryProvider = new H2QueryProvider(DB_NAME);
|
||||||
|
|
||||||
try {
|
DeletableBulletinBoardServer server = new BulletinBoardSQLServer(queryProvider);
|
||||||
|
server.init();
|
||||||
Connection conn = queryProvider.getDataSource().getConnection();
|
|
||||||
Statement stmt = conn.createStatement();
|
|
||||||
|
|
||||||
List<String> 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);
|
|
||||||
|
|
||||||
LocalBulletinBoardClient client = new LocalBulletinBoardClient(server, THREAD_NUM, SUBSRCIPTION_DELAY);
|
LocalBulletinBoardClient client = new LocalBulletinBoardClient(server, THREAD_NUM, SUBSRCIPTION_DELAY);
|
||||||
subscriptionTester = new GenericSubscriptionClientTester(client);
|
subscriptionTester = new GenericSubscriptionClientTester(client);
|
||||||
clientTest = new GenericBulletinBoardClientTester(client);
|
clientTest = new GenericBulletinBoardClientTester(client, 98354);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,9 +59,9 @@ public class LocalBulletinBoardClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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
|
@Test
|
||||||
public void testSubscription() throws SignatureException, CommunicationException {
|
public void testSubscription() throws SignatureException, CommunicationException {
|
||||||
|
|
||||||
subscriptionTester.init();
|
subscriptionTester.init();
|
||||||
subscriptionTester.subscriptionTest();
|
subscriptionTester.subscriptionTest();
|
||||||
subscriptionTester.close();
|
subscriptionTester.close();
|
||||||
|
|
|
@ -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<String> 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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ public class ThreadedBulletinBoardClientIntegrationTest {
|
||||||
|
|
||||||
public ThreadedBulletinBoardClientIntegrationTest(){
|
public ThreadedBulletinBoardClientIntegrationTest(){
|
||||||
|
|
||||||
ThreadedBulletinBoardClient client = new ThreadedBulletinBoardClient();
|
ThreadedBulletinBoardClient client = new ThreadedBulletinBoardClient(3,0,500);
|
||||||
|
|
||||||
List<String> testDB = new LinkedList<>();
|
List<String> testDB = new LinkedList<>();
|
||||||
testDB.add(BASE_URL);
|
testDB.add(BASE_URL);
|
||||||
|
@ -38,7 +38,7 @@ public class ThreadedBulletinBoardClientIntegrationTest {
|
||||||
.setMinRedundancy((float) 1.0)
|
.setMinRedundancy((float) 1.0)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
clientTest = new GenericBulletinBoardClientTester(client);
|
clientTest = new GenericBulletinBoardClientTester(client, 52351);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,9 +66,9 @@ public class ThreadedBulletinBoardClientIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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 {
|
public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException {
|
||||||
|
|
||||||
clientTest.testBatchPost();
|
clientTest.testBatchPost();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -85,11 +86,4 @@ public class ThreadedBulletinBoardClientIntegrationTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInvalidBatchClose() throws CommunicationException, InterruptedException {
|
|
||||||
|
|
||||||
clientTest.testInvalidBatchClose();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
/bin/
|
/bin/
|
||||||
|
|
|
@ -259,4 +259,3 @@ publishing {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<root>
|
<root>
|
||||||
<setting name="task-tab">
|
<setting name="task-tab">
|
||||||
<setting name="show-description" value="true"/>
|
<setting name="show-description" value="true"/>
|
||||||
</setting>
|
</setting>
|
||||||
<setting name="favorites-tab"/>
|
<setting name="favorites-tab"/>
|
||||||
<setting name="command_line-tab"/>
|
<setting name="command_line-tab"/>
|
||||||
<setting name="setup-tab">
|
<setting name="setup-tab">
|
||||||
<setting name="setup">
|
<setting name="setup">
|
||||||
<setting name="custom-gradle-executor"/>
|
<setting name="custom-gradle-executor"/>
|
||||||
<setting name="current-directory" value="/home/talm/proj/meerkat/bulletin-board-server"/>
|
<setting name="current-directory" value="/home/talm/proj/meerkat/bulletin-board-server"/>
|
||||||
<setting name="log-level" value="LIFECYCLE"/>
|
<setting name="log-level" value="LIFECYCLE"/>
|
||||||
</setting>
|
</setting>
|
||||||
</setting>
|
</setting>
|
||||||
<setting name="SinglePaneUIInstance_splitter-id">
|
<setting name="SinglePaneUIInstance_splitter-id">
|
||||||
<setting name="divider_location" value="387"/>
|
<setting name="divider_location" value="387"/>
|
||||||
</setting>
|
</setting>
|
||||||
<setting name="main_panel">
|
<setting name="main_panel">
|
||||||
<setting name="current-tab" value="Task Tree"/>
|
<setting name="current-tab" value="Task Tree"/>
|
||||||
</setting>
|
</setting>
|
||||||
<setting name="Application_window-id">
|
<setting name="Application_window-id">
|
||||||
<setting name="window_x" value="306"/>
|
<setting name="window_x" value="306"/>
|
||||||
<setting name="window_y" value="1042"/>
|
<setting name="window_y" value="1042"/>
|
||||||
<setting name="window_width" value="800"/>
|
<setting name="window_width" value="800"/>
|
||||||
<setting name="window_height" value="800"/>
|
<setting name="window_height" value="800"/>
|
||||||
<setting name="extended-state" value="0"/>
|
<setting name="extended-state" value="0"/>
|
||||||
</setting>
|
</setting>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
package meerkat.bulletinboard.service;
|
package meerkat.bulletinboard.service;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.Message;
|
import com.google.protobuf.Message;
|
||||||
import meerkat.protobuf.Crypto;
|
import meerkat.protobuf.Crypto;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by talm on 10/11/15.
|
* Created by talm on 10/11/15.
|
||||||
*/
|
*/
|
||||||
public class HelloProtoBuf {
|
public class HelloProtoBuf {
|
||||||
public Message sayHello() {
|
public Message sayHello() {
|
||||||
BulletinBoardMessage.Builder msg = BulletinBoardMessage.newBuilder();
|
BulletinBoardMessage.Builder msg = BulletinBoardMessage.newBuilder();
|
||||||
|
|
||||||
UnsignedBulletinBoardMessage.Builder unsigned = UnsignedBulletinBoardMessage.newBuilder();
|
UnsignedBulletinBoardMessage.Builder unsigned = UnsignedBulletinBoardMessage.newBuilder();
|
||||||
unsigned.setData(ByteString.copyFromUtf8("Hello World!"));
|
unsigned.setData(ByteString.copyFromUtf8("Hello World!"));
|
||||||
List<String> tags = Arrays.asList("Greetings", "FirstPrograms");
|
List<String> tags = Arrays.asList("Greetings", "FirstPrograms");
|
||||||
unsigned.addAllTag(tags);
|
unsigned.addAllTag(tags);
|
||||||
msg.setMsg(unsigned);
|
msg.setMsg(unsigned);
|
||||||
|
|
||||||
Crypto.Signature.Builder sig = Crypto.Signature.newBuilder();
|
Crypto.Signature.Builder sig = Crypto.Signature.newBuilder();
|
||||||
sig.setData(ByteString.copyFromUtf8("deadbeef"));
|
sig.setData(ByteString.copyFromUtf8("deadbeef"));
|
||||||
msg.addSig(sig);
|
msg.addSig(sig);
|
||||||
|
|
||||||
return msg.build();
|
return msg.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,266 +1,291 @@
|
||||||
package meerkat.bulletinboard.sqlserver;
|
package meerkat.bulletinboard.sqlserver;
|
||||||
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.FilterType;
|
import meerkat.protobuf.BulletinBoardAPI.FilterType;
|
||||||
import org.apache.commons.dbcp2.BasicDataSource;
|
import org.apache.commons.dbcp2.BasicDataSource;
|
||||||
import org.h2.jdbcx.JdbcDataSource;
|
|
||||||
import javax.naming.Context;
|
import javax.sql.DataSource;
|
||||||
import javax.naming.InitialContext;
|
import java.text.MessageFormat;
|
||||||
|
import java.util.LinkedList;
|
||||||
import javax.naming.NamingException;
|
import java.util.List;
|
||||||
import javax.sql.DataSource;
|
|
||||||
import java.text.MessageFormat;
|
/**
|
||||||
import java.util.LinkedList;
|
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||||
import java.util.List;
|
*/
|
||||||
|
|
||||||
/**
|
public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider {
|
||||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
|
||||||
*/
|
private String dbName;
|
||||||
|
|
||||||
public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider {
|
public H2QueryProvider(String dbName) {
|
||||||
|
this.dbName = dbName;
|
||||||
private String dbName;
|
}
|
||||||
|
|
||||||
public H2QueryProvider(String dbName) {
|
|
||||||
this.dbName = dbName;
|
@Override
|
||||||
}
|
public String getSQLString(QueryType queryType) throws IllegalArgumentException{
|
||||||
|
|
||||||
|
switch(queryType) {
|
||||||
@Override
|
case ADD_SIGNATURE:
|
||||||
public String getSQLString(QueryType queryType) throws IllegalArgumentException{
|
return MessageFormat.format(
|
||||||
|
"INSERT INTO SignatureTable (EntryNum, SignerId, Signature)"
|
||||||
switch(queryType) {
|
+ " SELECT DISTINCT :{0} AS Entry, :{1} AS Id, :{2} AS Sig FROM UtilityTable AS Temp"
|
||||||
case ADD_SIGNATURE:
|
+ " WHERE NOT EXISTS"
|
||||||
return "INSERT INTO SignatureTable (EntryNum, SignerId, Signature)"
|
+ " (SELECT 1 FROM SignatureTable AS SubTable WHERE SubTable.EntryNum = :{0} AND SubTable.SignerId = :{1})",
|
||||||
+ " SELECT DISTINCT :EntryNum AS Entry, :SignerId AS Id, :Signature AS Sig FROM UtilityTable AS Temp"
|
QueryType.ADD_SIGNATURE.getParamName(0),
|
||||||
+ " WHERE NOT EXISTS"
|
QueryType.ADD_SIGNATURE.getParamName(1),
|
||||||
+ " (SELECT 1 FROM SignatureTable AS SubTable WHERE SubTable.SignerId = :SignerId AND SubTable.EntryNum = :EntryNum)";
|
QueryType.ADD_SIGNATURE.getParamName(2));
|
||||||
|
|
||||||
case CONNECT_TAG:
|
case CONNECT_TAG:
|
||||||
return "INSERT INTO MsgTagTable (TagId, EntryNum)"
|
return MessageFormat.format(
|
||||||
+ " SELECT DISTINCT TagTable.TagId, :EntryNum AS NewEntry FROM TagTable WHERE Tag = :Tag"
|
"INSERT INTO MsgTagTable (TagId, EntryNum)"
|
||||||
+ " AND NOT EXISTS (SELECT 1 FROM MsgTagTable AS SubTable WHERE SubTable.TagId = TagTable.TagId"
|
+ " SELECT DISTINCT TagTable.TagId, :{0} AS NewEntry FROM TagTable WHERE Tag = :{1}"
|
||||||
+ " AND SubTable.EntryNum = :EntryNum)";
|
+ " AND NOT EXISTS (SELECT 1 FROM MsgTagTable AS SubTable WHERE SubTable.TagId = TagTable.TagId"
|
||||||
|
+ " AND SubTable.EntryNum = :{0})",
|
||||||
case FIND_MSG_ID:
|
QueryType.CONNECT_TAG.getParamName(0),
|
||||||
return "SELECT EntryNum From MsgTable WHERE MsgId = :MsgId";
|
QueryType.CONNECT_TAG.getParamName(1));
|
||||||
|
|
||||||
case FIND_TAG_ID:
|
case FIND_MSG_ID:
|
||||||
return MessageFormat.format(
|
return MessageFormat.format(
|
||||||
"SELECT TagId FROM TagTable WHERE Tag = :{0}",
|
"SELECT EntryNum From MsgTable WHERE MsgId = :{0}",
|
||||||
QueryType.FIND_TAG_ID.getParamName(0));
|
QueryType.FIND_MSG_ID.getParamName(0));
|
||||||
|
|
||||||
case GET_MESSAGES:
|
case FIND_TAG_ID:
|
||||||
return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
|
return MessageFormat.format(
|
||||||
|
"SELECT TagId FROM TagTable WHERE Tag = :{0}",
|
||||||
case COUNT_MESSAGES:
|
QueryType.FIND_TAG_ID.getParamName(0));
|
||||||
return "SELECT COUNT(MsgTable.EntryNum) FROM MsgTable";
|
|
||||||
|
case GET_MESSAGES:
|
||||||
case GET_MESSAGE_STUBS:
|
return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
|
||||||
return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable";
|
|
||||||
|
case COUNT_MESSAGES:
|
||||||
case GET_SIGNATURES:
|
return "SELECT COUNT(MsgTable.EntryNum) FROM MsgTable";
|
||||||
return "SELECT Signature FROM SignatureTable WHERE EntryNum = :EntryNum";
|
|
||||||
|
case GET_MESSAGE_STUBS:
|
||||||
case INSERT_MSG:
|
return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable";
|
||||||
return "INSERT INTO MsgTable (MsgId, Msg, ExactTime) VALUES(:MsgId,:Msg,:TimeStamp)";
|
|
||||||
|
case GET_SIGNATURES:
|
||||||
case INSERT_NEW_TAG:
|
return MessageFormat.format(
|
||||||
return "INSERT INTO TagTable(Tag) SELECT DISTINCT :Tag AS NewTag FROM UtilityTable WHERE"
|
"SELECT Signature FROM SignatureTable WHERE EntryNum = :{0}",
|
||||||
+ " NOT EXISTS (SELECT 1 FROM TagTable AS SubTable WHERE SubTable.Tag = :Tag)";
|
QueryType.GET_SIGNATURES.getParamName(0));
|
||||||
|
|
||||||
case GET_LAST_MESSAGE_ENTRY:
|
case INSERT_MSG:
|
||||||
return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable";
|
return MessageFormat.format(
|
||||||
|
"INSERT INTO MsgTable (MsgId, ExactTime, Msg) VALUES(:{0}, :{1}, :{2})",
|
||||||
case GET_BATCH_MESSAGE_ENTRY:
|
QueryType.INSERT_MSG.getParamName(0),
|
||||||
return MessageFormat.format(
|
QueryType.INSERT_MSG.getParamName(1),
|
||||||
"SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"
|
QueryType.INSERT_MSG.getParamName(2));
|
||||||
+ " INNER JOIN SignatureTable ON MsgTable.EntryNum = SignatureTable.EntryNum"
|
|
||||||
+ " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum"
|
case DELETE_MSG_BY_ENTRY:
|
||||||
+ " INNER JOIN TagTable ON MsgTagTable.TagId = TagTable.TagId"
|
return MessageFormat.format(
|
||||||
+ " WHERE SignatureTable.SignerId = :{0}"
|
"DELETE FROM MsgTable WHERE EntryNum = :{0}",
|
||||||
+ " AND TagTable.Tag = :{1}",
|
QueryType.DELETE_MSG_BY_ENTRY.getParamName(0));
|
||||||
QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0),
|
|
||||||
QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1));
|
case DELETE_MSG_BY_ID:
|
||||||
|
return MessageFormat.format(
|
||||||
case GET_BATCH_MESSAGE_DATA:
|
"DELETE FROM MsgTable WHERE MsgId = :{0}",
|
||||||
return MessageFormat.format(
|
QueryType.DELETE_MSG_BY_ID.getParamName(0));
|
||||||
"SELECT Data FROM BatchTable"
|
|
||||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}"
|
case INSERT_NEW_TAG:
|
||||||
+ " ORDER BY SerialNum ASC",
|
return MessageFormat.format(
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
|
"INSERT INTO TagTable(Tag) SELECT DISTINCT :Tag AS NewTag FROM UtilityTable WHERE"
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),
|
+ " NOT EXISTS (SELECT 1 FROM TagTable AS SubTable WHERE SubTable.Tag = :{0})",
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2));
|
QueryType.INSERT_NEW_TAG.getParamName(0));
|
||||||
|
|
||||||
case INSERT_BATCH_DATA:
|
case GET_LAST_MESSAGE_ENTRY:
|
||||||
return MessageFormat.format(
|
return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable";
|
||||||
"INSERT INTO BatchTable (SignerId, BatchId, SerialNum, Data)"
|
|
||||||
+ " VALUES (:{0}, :{1}, :{2}, :{3})",
|
case GET_BATCH_MESSAGE_DATA_BY_MSG_ID:
|
||||||
QueryType.INSERT_BATCH_DATA.getParamName(0),
|
return MessageFormat.format(
|
||||||
QueryType.INSERT_BATCH_DATA.getParamName(1),
|
"SELECT Data FROM BatchTable"
|
||||||
QueryType.INSERT_BATCH_DATA.getParamName(2),
|
+ " INNER JOIN MsgTable ON MsgTable.EntryNum = BatchTable.EntryNum"
|
||||||
QueryType.INSERT_BATCH_DATA.getParamName(3));
|
+ " WHERE MsgTable.MsgId = :{0} AND BatchTable.SerialNum >= :{1}"
|
||||||
|
+ " ORDER BY BatchTable.SerialNum ASC",
|
||||||
case CHECK_BATCH_LENGTH:
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(0),
|
||||||
return MessageFormat.format(
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(1));
|
||||||
"SELECT COUNT(Data) AS BatchLength FROM BatchTable"
|
|
||||||
+ " WHERE SignerId = :{0} AND BatchId = :{1}",
|
case GET_BATCH_MESSAGE_DATA_BY_BATCH_ID:
|
||||||
QueryType.CHECK_BATCH_LENGTH.getParamName(0),
|
return MessageFormat.format(
|
||||||
QueryType.CHECK_BATCH_LENGTH.getParamName(1));
|
"SELECT Data FROM BatchTable"
|
||||||
|
+ " WHERE BatchId = :{0} AND SerialNum >= :{1}"
|
||||||
case CONNECT_BATCH_TAG:
|
+ " ORDER BY BatchTable.SerialNum ASC",
|
||||||
return MessageFormat.format(
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(0),
|
||||||
"INSERT INTO BatchTagTable (SignerId, BatchId, TagId) SELECT :{0}, :{1}, TagId FROM TagTable"
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(1));
|
||||||
+ " WHERE Tag = :{2}",
|
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(0),
|
case INSERT_BATCH_DATA:
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(1),
|
return MessageFormat.format(
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(2));
|
"INSERT INTO BatchTable (BatchId, SerialNum, Data) VALUES (:{0}, :{1}, :{2})",
|
||||||
|
QueryType.INSERT_BATCH_DATA.getParamName(0),
|
||||||
case GET_BATCH_TAGS:
|
QueryType.INSERT_BATCH_DATA.getParamName(1),
|
||||||
return MessageFormat.format(
|
QueryType.INSERT_BATCH_DATA.getParamName(2));
|
||||||
"SELECT Tag FROM TagTable INNER JOIN BatchTagTable ON TagTable.TagId = BatchTagTable.TagId"
|
|
||||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} ORDER BY Tag ASC",
|
case CHECK_BATCH_LENGTH:
|
||||||
QueryType.GET_BATCH_TAGS.getParamName(0),
|
return MessageFormat.format(
|
||||||
QueryType.GET_BATCH_TAGS.getParamName(1));
|
"SELECT COUNT(Data) AS BatchLength FROM BatchTable WHERE BatchId = :{0}",
|
||||||
|
QueryType.CHECK_BATCH_LENGTH.getParamName(0));
|
||||||
case REMOVE_BATCH_TAGS:
|
|
||||||
return MessageFormat.format(
|
case CHECK_BATCH_OPEN:
|
||||||
"DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}",
|
return MessageFormat.format(
|
||||||
QueryType.REMOVE_BATCH_TAGS.getParamName(0),
|
"SELECT COUNT(BatchId) AS batchCount FROM BatchTagTable WHERE BatchId = :{0}",
|
||||||
QueryType.REMOVE_BATCH_TAGS.getParamName(1));
|
QueryType.CHECK_BATCH_OPEN.getParamName(0));
|
||||||
|
|
||||||
default:
|
case STORE_BATCH_TAGS:
|
||||||
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
return MessageFormat.format(
|
||||||
}
|
"INSERT INTO BatchTagTable (Tags) VALUES (:{0})",
|
||||||
|
QueryType.STORE_BATCH_TAGS.getParamName(0));
|
||||||
}
|
|
||||||
|
case GET_BATCH_TAGS:
|
||||||
@Override
|
return MessageFormat.format(
|
||||||
public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException {
|
"SELECT Tags FROM BatchTagTable WHERE BatchId = :{0}",
|
||||||
|
QueryType.GET_BATCH_TAGS.getParamName(0));
|
||||||
String serialString = Integer.toString(serialNum);
|
|
||||||
|
case ADD_ENTRY_NUM_TO_BATCH:
|
||||||
switch(filterType) {
|
return MessageFormat.format(
|
||||||
case EXACT_ENTRY:
|
"UPDATE BatchTable SET EntryNum = :{1} WHERE BatchId = :{0}",
|
||||||
return "MsgTable.EntryNum = :EntryNum" + serialString;
|
QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(0),
|
||||||
case MAX_ENTRY:
|
QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1));
|
||||||
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
|
||||||
case MIN_ENTRY:
|
default:
|
||||||
return "MsgTable.EntryNum >= :EntryNum" + serialString;
|
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
||||||
case MAX_MESSAGES:
|
}
|
||||||
return "LIMIT :Limit" + serialString;
|
|
||||||
case MSG_ID:
|
}
|
||||||
return "MsgTable.MsgId = :MsgId" + serialString;
|
|
||||||
case SIGNER_ID:
|
@Override
|
||||||
return "EXISTS (SELECT 1 FROM SignatureTable"
|
public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException {
|
||||||
+ " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)";
|
|
||||||
case TAG:
|
String serialString = Integer.toString(serialNum);
|
||||||
return "EXISTS (SELECT 1 FROM TagTable"
|
|
||||||
+ " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId"
|
switch(filterType) {
|
||||||
+ " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)";
|
case EXACT_ENTRY:
|
||||||
|
return "MsgTable.EntryNum = :EntryNum" + serialString;
|
||||||
case BEFORE_TIME:
|
case MAX_ENTRY:
|
||||||
return "MsgTable.ExactTime <= :TimeStamp" + serialString;
|
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
||||||
|
case MIN_ENTRY:
|
||||||
case AFTER_TIME:
|
return "MsgTable.EntryNum >= :EntryNum" + serialString;
|
||||||
return "MsgTable.ExactTime >= :TimeStamp" + serialString;
|
case MAX_MESSAGES:
|
||||||
|
return "LIMIT :Limit" + serialString;
|
||||||
default:
|
case MSG_ID:
|
||||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
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:
|
||||||
@Override
|
return "EXISTS (SELECT 1 FROM TagTable"
|
||||||
public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException {
|
+ " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId"
|
||||||
|
+ " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)";
|
||||||
switch(filterType) {
|
|
||||||
case EXACT_ENTRY: // Go through
|
case BEFORE_TIME:
|
||||||
case MAX_ENTRY: // Go through
|
return "MsgTable.ExactTime <= :TimeStamp" + serialString;
|
||||||
case MIN_ENTRY: // Go through
|
|
||||||
case MAX_MESSAGES:
|
case AFTER_TIME:
|
||||||
return "INT";
|
return "MsgTable.ExactTime >= :TimeStamp" + serialString;
|
||||||
|
|
||||||
case MSG_ID: // Go through
|
default:
|
||||||
case SIGNER_ID:
|
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||||
return "TINYBLOB";
|
}
|
||||||
|
|
||||||
case TAG:
|
}
|
||||||
return "VARCHAR";
|
|
||||||
|
@Override
|
||||||
case AFTER_TIME: // Go through
|
public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException {
|
||||||
case BEFORE_TIME:
|
|
||||||
return "TIMESTAMP";
|
switch(filterType) {
|
||||||
|
case EXACT_ENTRY: // Go through
|
||||||
|
case MAX_ENTRY: // Go through
|
||||||
default:
|
case MIN_ENTRY: // Go through
|
||||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
case MAX_MESSAGES:
|
||||||
}
|
return "INT";
|
||||||
|
|
||||||
}
|
case MSG_ID: // Go through
|
||||||
|
case SIGNER_ID:
|
||||||
@Override
|
return "TINYBLOB";
|
||||||
public DataSource getDataSource() {
|
|
||||||
|
case TAG:
|
||||||
BasicDataSource dataSource = new BasicDataSource();
|
return "VARCHAR";
|
||||||
|
|
||||||
dataSource.setDriverClassName("org.h2.Driver");
|
case AFTER_TIME: // Go through
|
||||||
dataSource.setUrl("jdbc:h2:~/" + dbName);
|
case BEFORE_TIME:
|
||||||
|
return "TIMESTAMP";
|
||||||
return dataSource;
|
|
||||||
|
|
||||||
}
|
default:
|
||||||
|
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public List<String> getSchemaCreationCommands() {
|
}
|
||||||
List<String> list = new LinkedList<String>();
|
|
||||||
|
@Override
|
||||||
list.add("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INT NOT NULL AUTO_INCREMENT PRIMARY KEY, MsgId TINYBLOB UNIQUE, ExactTime TIMESTAMP, Msg BLOB)");
|
public DataSource getDataSource() {
|
||||||
|
|
||||||
list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INT NOT NULL AUTO_INCREMENT PRIMARY KEY, Tag VARCHAR(50) UNIQUE)");
|
BasicDataSource dataSource = new BasicDataSource();
|
||||||
|
|
||||||
list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum INT, TagId INT,"
|
dataSource.setDriverClassName("org.h2.Driver");
|
||||||
+ " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum),"
|
dataSource.setUrl("jdbc:h2:mem:" + dbName);
|
||||||
+ " FOREIGN KEY (TagId) REFERENCES TagTable(TagId),"
|
|
||||||
+ " UNIQUE (EntryNum, TagID))");
|
return dataSource;
|
||||||
|
|
||||||
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)");
|
@Override
|
||||||
list.add("CREATE UNIQUE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId, EntryNum)");
|
public List<String> getSchemaCreationCommands() {
|
||||||
|
List<String> list = new LinkedList<String>();
|
||||||
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 MsgTable (EntryNum INT NOT NULL AUTO_INCREMENT PRIMARY KEY,"
|
||||||
|
+ " MsgId TINYBLOB UNIQUE, ExactTime TIMESTAMP, Msg BLOB)");
|
||||||
list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, TagId INT,"
|
|
||||||
+ " FOREIGN KEY (TagId) REFERENCES TagTable(TagId))");
|
list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INT NOT NULL AUTO_INCREMENT PRIMARY KEY, Tag VARCHAR(50) UNIQUE)");
|
||||||
|
|
||||||
list.add("CREATE INDEX IF NOT EXISTS BatchIndex ON BatchTagTable(SignerId, BatchId)");
|
list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum INT, TagId INT,"
|
||||||
|
+ " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE,"
|
||||||
// This is used to create a simple table with one entry.
|
+ " FOREIGN KEY (TagId) REFERENCES TagTable(TagId) ON DELETE CASCADE,"
|
||||||
// It is used for implementing a workaround for the missing INSERT IGNORE syntax
|
+ " UNIQUE (EntryNum, TagID))");
|
||||||
list.add("CREATE TABLE IF NOT EXISTS UtilityTable (Entry INT)");
|
|
||||||
list.add("INSERT INTO UtilityTable (Entry) VALUES (1)");
|
list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INT, SignerId TINYBLOB, Signature TINYBLOB UNIQUE,"
|
||||||
|
+ " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE)");
|
||||||
return list;
|
|
||||||
}
|
list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)");
|
||||||
|
list.add("CREATE UNIQUE INDEX IF NOT EXISTS SignatureIndex ON SignatureTable(SignerId, EntryNum)");
|
||||||
@Override
|
|
||||||
public List<String> getSchemaDeletionCommands() {
|
list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (BatchId INT AUTO_INCREMENT PRIMARY KEY, Tags BLOB)");
|
||||||
List<String> list = new LinkedList<String>();
|
|
||||||
|
list.add("CREATE TABLE IF NOT EXISTS BatchTable (BatchId INT, EntryNum INT, SerialNum INT, Data BLOB,"
|
||||||
list.add("DROP TABLE IF EXISTS UtilityTable");
|
+ " UNIQUE(BatchId, SerialNum),"
|
||||||
list.add("DROP INDEX IF EXISTS BatchIndex");
|
+ " FOREIGN KEY (BatchId) REFERENCES BatchTagTable(BatchId) ON DELETE CASCADE)");
|
||||||
list.add("DROP TABLE IF EXISTS BatchTagTable");
|
list.add("CREATE INDEX IF NOT EXISTS BatchDataIndex ON BatchTable(EntryNum, SerialNum)");
|
||||||
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");
|
// This is used to create a simple table with one entry.
|
||||||
list.add("DROP TABLE IF EXISTS TagTable");
|
// It is used for implementing a workaround for the missing INSERT IGNORE syntax
|
||||||
list.add("DROP TABLE IF EXISTS MsgTable");
|
list.add("CREATE TABLE IF NOT EXISTS UtilityTable (Entry INT)");
|
||||||
|
list.add("INSERT INTO UtilityTable (Entry) VALUES (1)");
|
||||||
return list;
|
|
||||||
}
|
return list;
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
|
public List<String> getSchemaDeletionCommands() {
|
||||||
|
List<String> list = new LinkedList<String>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,273 +1,276 @@
|
||||||
package meerkat.bulletinboard.sqlserver;
|
package meerkat.bulletinboard.sqlserver;
|
||||||
|
|
||||||
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
|
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider;
|
||||||
import meerkat.bulletinboard.BulletinBoardConstants;
|
import meerkat.protobuf.BulletinBoardAPI.FilterType;
|
||||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider;
|
import org.apache.commons.dbcp2.BasicDataSource;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.FilterType;
|
|
||||||
import org.apache.commons.dbcp2.BasicDataSource;
|
import javax.sql.DataSource;
|
||||||
|
import java.text.MessageFormat;
|
||||||
import javax.sql.DataSource;
|
import java.util.LinkedList;
|
||||||
import java.text.MessageFormat;
|
import java.util.List;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
/**
|
||||||
|
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||||
/**
|
*/
|
||||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
|
||||||
*/
|
public class MySQLQueryProvider implements SQLQueryProvider {
|
||||||
|
|
||||||
public class MySQLQueryProvider implements SQLQueryProvider {
|
private String dbAddress;
|
||||||
|
private int dbPort;
|
||||||
private String dbAddress;
|
private String dbName;
|
||||||
private int dbPort;
|
private String username;
|
||||||
private String dbName;
|
private String password;
|
||||||
private String username;
|
|
||||||
private String password;
|
public MySQLQueryProvider(String dbAddress, int dbPort, String dbName, String username, String password) {
|
||||||
|
this.dbAddress = dbAddress;
|
||||||
public MySQLQueryProvider(String dbAddress, int dbPort, String dbName, String username, String password) {
|
this.dbPort = dbPort;
|
||||||
this.dbAddress = dbAddress;
|
this.dbName = dbName;
|
||||||
this.dbPort = dbPort;
|
this.username = username;
|
||||||
this.dbName = dbName;
|
this.password = password;
|
||||||
this.username = username;
|
}
|
||||||
this.password = password;
|
|
||||||
}
|
@Override
|
||||||
|
public String getSQLString(QueryType queryType) throws IllegalArgumentException{
|
||||||
@Override
|
|
||||||
public String getSQLString(QueryType queryType) throws IllegalArgumentException{
|
switch(queryType) {
|
||||||
|
|
||||||
switch(queryType) {
|
case ADD_SIGNATURE:
|
||||||
|
return MessageFormat.format(
|
||||||
case ADD_SIGNATURE:
|
"INSERT IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (:{0}, :{1}, :{2})",
|
||||||
return MessageFormat.format(
|
QueryType.ADD_SIGNATURE.getParamName(0),
|
||||||
"INSERT IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (:{0}, :{1}, :{2})",
|
QueryType.ADD_SIGNATURE.getParamName(1),
|
||||||
QueryType.ADD_SIGNATURE.getParamName(0),
|
QueryType.ADD_SIGNATURE.getParamName(2));
|
||||||
QueryType.ADD_SIGNATURE.getParamName(1),
|
|
||||||
QueryType.ADD_SIGNATURE.getParamName(2));
|
case CONNECT_TAG:
|
||||||
|
return MessageFormat.format(
|
||||||
case CONNECT_TAG:
|
"INSERT IGNORE INTO MsgTagTable (TagId, EntryNum)"
|
||||||
return MessageFormat.format(
|
+ " SELECT TagTable.TagId, :{0} AS EntryNum FROM TagTable WHERE Tag = :{1}",
|
||||||
"INSERT IGNORE INTO MsgTagTable (TagId, EntryNum)"
|
QueryType.CONNECT_TAG.getParamName(0),
|
||||||
+ " SELECT TagTable.TagId, :{0} AS EntryNum FROM TagTable WHERE Tag = :{1}",
|
QueryType.CONNECT_TAG.getParamName(1));
|
||||||
QueryType.CONNECT_TAG.getParamName(0),
|
|
||||||
QueryType.CONNECT_TAG.getParamName(1));
|
case FIND_MSG_ID:
|
||||||
|
return MessageFormat.format(
|
||||||
case FIND_MSG_ID:
|
"SELECT EntryNum From MsgTable WHERE MsgId = :{0}",
|
||||||
return MessageFormat.format(
|
QueryType.FIND_MSG_ID.getParamName(0));
|
||||||
"SELECT EntryNum From MsgTable WHERE MsgId = :{0}",
|
|
||||||
QueryType.FIND_MSG_ID.getParamName(0));
|
case FIND_TAG_ID:
|
||||||
|
return MessageFormat.format(
|
||||||
case FIND_TAG_ID:
|
"SELECT TagId FROM TagTable WHERE Tag = :{0}",
|
||||||
return MessageFormat.format(
|
QueryType.FIND_TAG_ID.getParamName(0));
|
||||||
"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 GET_MESSAGES:
|
|
||||||
return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
|
case COUNT_MESSAGES:
|
||||||
|
return "SELECT COUNT(MsgTable.EntryNum) 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_MESSAGE_STUBS:
|
|
||||||
return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable";
|
case GET_SIGNATURES:
|
||||||
|
return MessageFormat.format(
|
||||||
case GET_SIGNATURES:
|
"SELECT Signature FROM SignatureTable WHERE EntryNum = :{0}",
|
||||||
return MessageFormat.format(
|
QueryType.GET_SIGNATURES.getParamName(0));
|
||||||
"SELECT Signature FROM SignatureTable WHERE EntryNum = :{0}",
|
|
||||||
QueryType.GET_SIGNATURES.getParamName(0));
|
case INSERT_MSG:
|
||||||
|
return MessageFormat.format(
|
||||||
case INSERT_MSG:
|
"INSERT INTO MsgTable (MsgId, ExactTime, Msg) VALUES(:{0}, :{1}, :{2})",
|
||||||
return MessageFormat.format(
|
QueryType.INSERT_MSG.getParamName(0),
|
||||||
"INSERT INTO MsgTable (MsgId, ExactTime, Msg) VALUES(:{0}, :{1}, :{2})",
|
QueryType.INSERT_MSG.getParamName(1),
|
||||||
QueryType.INSERT_MSG.getParamName(0),
|
QueryType.INSERT_MSG.getParamName(2));
|
||||||
QueryType.INSERT_MSG.getParamName(1),
|
|
||||||
QueryType.INSERT_MSG.getParamName(2));
|
case DELETE_MSG_BY_ENTRY:
|
||||||
|
return MessageFormat.format(
|
||||||
case INSERT_NEW_TAG:
|
"DELETE IGNORE FROM MsgTable WHERE EntryNum = :{0}",
|
||||||
return MessageFormat.format(
|
QueryType.DELETE_MSG_BY_ENTRY.getParamName(0));
|
||||||
"INSERT IGNORE INTO TagTable(Tag) VALUES (:{0})",
|
|
||||||
QueryType.INSERT_NEW_TAG.getParamName(0));
|
case DELETE_MSG_BY_ID:
|
||||||
|
return MessageFormat.format(
|
||||||
case GET_LAST_MESSAGE_ENTRY:
|
"DELETE IGNORE FROM MsgTable WHERE MsgId = :{0}",
|
||||||
return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable";
|
QueryType.DELETE_MSG_BY_ID.getParamName(0));
|
||||||
|
|
||||||
case GET_BATCH_MESSAGE_ENTRY:
|
case INSERT_NEW_TAG:
|
||||||
return MessageFormat.format(
|
return MessageFormat.format(
|
||||||
"SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"
|
"INSERT IGNORE INTO TagTable(Tag) VALUES (:{0})",
|
||||||
+ " INNER JOIN SignatureTable ON MsgTable.EntryNum = SignatureTable.EntryNum"
|
QueryType.INSERT_NEW_TAG.getParamName(0));
|
||||||
+ " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum"
|
|
||||||
+ " INNER JOIN TagTable ON MsgTagTable.TagId = TagTable.TagId"
|
case GET_LAST_MESSAGE_ENTRY:
|
||||||
+ " WHERE SignatureTable.SignerId = :{0}"
|
return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable";
|
||||||
+ " AND TagTable.Tag = :{1}",
|
|
||||||
QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0),
|
case GET_BATCH_MESSAGE_DATA_BY_MSG_ID:
|
||||||
QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1));
|
return MessageFormat.format(
|
||||||
|
"SELECT Data FROM BatchTable"
|
||||||
case GET_BATCH_MESSAGE_DATA:
|
+ " INNER JOIN MsgTable ON MsgTable.EntryNum = BatchTable.EntryNum"
|
||||||
return MessageFormat.format(
|
+ " WHERE MsgTable.MsgId = :{0} AND BatchTable.SerialNum >= :{1}"
|
||||||
"SELECT Data FROM BatchTable"
|
+ " ORDER BY BatchTable.SerialNum ASC",
|
||||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}"
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(0),
|
||||||
+ " ORDER BY SerialNum ASC",
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(1));
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
|
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),
|
case GET_BATCH_MESSAGE_DATA_BY_BATCH_ID:
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2));
|
return MessageFormat.format(
|
||||||
|
"SELECT Data FROM BatchTable"
|
||||||
case INSERT_BATCH_DATA:
|
+ " WHERE BatchId = :{0} AND SerialNum >= :{1}"
|
||||||
return MessageFormat.format(
|
+ " ORDER BY BatchTable.SerialNum ASC",
|
||||||
"INSERT INTO BatchTable (SignerId, BatchId, SerialNum, Data)"
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(0),
|
||||||
+ " VALUES (:{0}, :{1}, :{2}, :{3})",
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(1));
|
||||||
QueryType.INSERT_BATCH_DATA.getParamName(0),
|
|
||||||
QueryType.INSERT_BATCH_DATA.getParamName(1),
|
case INSERT_BATCH_DATA:
|
||||||
QueryType.INSERT_BATCH_DATA.getParamName(2),
|
return MessageFormat.format(
|
||||||
QueryType.INSERT_BATCH_DATA.getParamName(3));
|
"INSERT INTO BatchTable (BatchId, SerialNum, Data) VALUES (:{0}, :{1}, :{2})",
|
||||||
|
QueryType.INSERT_BATCH_DATA.getParamName(0),
|
||||||
case CHECK_BATCH_LENGTH:
|
QueryType.INSERT_BATCH_DATA.getParamName(1),
|
||||||
return MessageFormat.format(
|
QueryType.INSERT_BATCH_DATA.getParamName(2));
|
||||||
"SELECT COUNT(Data) AS BatchLength FROM BatchTable"
|
|
||||||
+ " WHERE SignerId = :{0} AND BatchId = :{1}",
|
case CHECK_BATCH_LENGTH:
|
||||||
QueryType.CHECK_BATCH_LENGTH.getParamName(0),
|
return MessageFormat.format(
|
||||||
QueryType.CHECK_BATCH_LENGTH.getParamName(1));
|
"SELECT COUNT(Data) AS BatchLength FROM BatchTable WHERE BatchId = :{0}",
|
||||||
|
QueryType.CHECK_BATCH_LENGTH.getParamName(0));
|
||||||
case CONNECT_BATCH_TAG:
|
|
||||||
return MessageFormat.format(
|
case CHECK_BATCH_OPEN:
|
||||||
"INSERT INTO BatchTagTable (SignerId, BatchId, TagId) SELECT :{0}, :{1}, TagId FROM TagTable"
|
return MessageFormat.format(
|
||||||
+ " WHERE Tag = :{2}",
|
"SELECT COUNT(BatchId) AS batchCount FROM BatchTagTable WHERE BatchId = :{0}",
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(0),
|
QueryType.CHECK_BATCH_OPEN.getParamName(0));
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(1),
|
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(2));
|
case STORE_BATCH_TAGS:
|
||||||
|
return MessageFormat.format(
|
||||||
case GET_BATCH_TAGS:
|
"INSERT INTO BatchTagTable (Tags) VALUES (:{0})",
|
||||||
return MessageFormat.format(
|
QueryType.STORE_BATCH_TAGS.getParamName(0));
|
||||||
"SELECT Tag FROM TagTable INNER JOIN BatchTagTable ON TagTable.TagId = BatchTagTable.TagId"
|
|
||||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} ORDER BY Tag ASC",
|
case GET_BATCH_TAGS:
|
||||||
QueryType.GET_BATCH_TAGS.getParamName(0),
|
return MessageFormat.format(
|
||||||
QueryType.GET_BATCH_TAGS.getParamName(1));
|
"SELECT Tags FROM BatchTagTable WHERE BatchId = :{0}",
|
||||||
|
QueryType.GET_BATCH_TAGS.getParamName(0));
|
||||||
case REMOVE_BATCH_TAGS:
|
|
||||||
return MessageFormat.format(
|
case ADD_ENTRY_NUM_TO_BATCH:
|
||||||
"DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}",
|
return MessageFormat.format(
|
||||||
QueryType.REMOVE_BATCH_TAGS.getParamName(0),
|
"UPDATE BatchTable SET EntryNum = :{1} WHERE BatchId = :{0}",
|
||||||
QueryType.REMOVE_BATCH_TAGS.getParamName(1));
|
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);
|
default:
|
||||||
}
|
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
||||||
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException {
|
@Override
|
||||||
|
public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException {
|
||||||
String serialString = Integer.toString(serialNum);
|
|
||||||
|
String serialString = Integer.toString(serialNum);
|
||||||
switch(filterType) {
|
|
||||||
case EXACT_ENTRY:
|
switch(filterType) {
|
||||||
return "MsgTable.EntryNum = :EntryNum" + serialString;
|
case EXACT_ENTRY:
|
||||||
case MAX_ENTRY:
|
return "MsgTable.EntryNum = :EntryNum" + serialString;
|
||||||
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
case MAX_ENTRY:
|
||||||
case MIN_ENTRY:
|
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
||||||
return "MsgTable.EntryNum >= :EntryNum" + serialString;
|
case MIN_ENTRY:
|
||||||
case MAX_MESSAGES:
|
return "MsgTable.EntryNum >= :EntryNum" + serialString;
|
||||||
return "LIMIT :Limit" + serialString;
|
case MAX_MESSAGES:
|
||||||
case MSG_ID:
|
return "LIMIT :Limit" + serialString;
|
||||||
return "MsgTable.MsgId = :MsgId" + serialString;
|
case MSG_ID:
|
||||||
case SIGNER_ID:
|
return "MsgTable.MsgId = :MsgId" + serialString;
|
||||||
return "EXISTS (SELECT 1 FROM SignatureTable"
|
case SIGNER_ID:
|
||||||
+ " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)";
|
return "EXISTS (SELECT 1 FROM SignatureTable"
|
||||||
case TAG:
|
+ " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)";
|
||||||
return "EXISTS (SELECT 1 FROM TagTable"
|
case TAG:
|
||||||
+ " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId"
|
return "EXISTS (SELECT 1 FROM TagTable"
|
||||||
+ " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)";
|
+ " 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 BEFORE_TIME:
|
||||||
|
return "MsgTable.ExactTime <= :TimeStamp";
|
||||||
case AFTER_TIME:
|
|
||||||
return "MsgTable.ExactTime >= :TimeStamp";
|
case AFTER_TIME:
|
||||||
|
return "MsgTable.ExactTime >= :TimeStamp";
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
default:
|
||||||
}
|
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||||
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException {
|
@Override
|
||||||
|
public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException {
|
||||||
switch(filterType) {
|
|
||||||
case EXACT_ENTRY: // Go through
|
switch(filterType) {
|
||||||
case MAX_ENTRY: // Go through
|
case EXACT_ENTRY: // Go through
|
||||||
case MIN_ENTRY: // Go through
|
case MAX_ENTRY: // Go through
|
||||||
case MAX_MESSAGES:
|
case MIN_ENTRY: // Go through
|
||||||
return "INT";
|
case MAX_MESSAGES:
|
||||||
|
return "INT";
|
||||||
case MSG_ID: // Go through
|
|
||||||
case SIGNER_ID:
|
case MSG_ID: // Go through
|
||||||
return "TINYBLOB";
|
case SIGNER_ID:
|
||||||
|
return "TINYBLOB";
|
||||||
case TAG:
|
|
||||||
return "VARCHAR";
|
case TAG:
|
||||||
|
return "VARCHAR";
|
||||||
case AFTER_TIME: // Go through
|
|
||||||
case BEFORE_TIME:
|
case AFTER_TIME: // Go through
|
||||||
return "TIMESTAMP";
|
case BEFORE_TIME:
|
||||||
|
return "TIMESTAMP";
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
default:
|
||||||
}
|
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||||
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public DataSource getDataSource() {
|
@Override
|
||||||
|
public DataSource getDataSource() {
|
||||||
BasicDataSource dataSource = new BasicDataSource();
|
|
||||||
|
BasicDataSource dataSource = new BasicDataSource();
|
||||||
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
|
|
||||||
dataSource.setUrl("jdbc:mysql://" + dbAddress + ":" + dbPort + "/" + dbName);
|
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
|
||||||
|
dataSource.setUrl("jdbc:mysql://" + dbAddress + ":" + dbPort + "/" + dbName);
|
||||||
dataSource.setUsername(username);
|
|
||||||
dataSource.setPassword(password);
|
dataSource.setUsername(username);
|
||||||
|
dataSource.setPassword(password);
|
||||||
return dataSource;
|
|
||||||
|
return dataSource;
|
||||||
}
|
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public List<String> getSchemaCreationCommands() {
|
@Override
|
||||||
List<String> list = new LinkedList<String>();
|
public List<String> getSchemaCreationCommands() {
|
||||||
|
List<String> list = new LinkedList<String>();
|
||||||
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 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 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),"
|
list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum INT, TagId INT,"
|
||||||
+ " CONSTRAINT FOREIGN KEY (TagId) REFERENCES TagTable(TagId),"
|
+ " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE,"
|
||||||
+ " CONSTRAINT UNIQUE (EntryNum, TagID))");
|
+ " 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),"
|
list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INT, SignerId TINYBLOB, Signature TINYBLOB,"
|
||||||
+ " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))");
|
+ " 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 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 (BatchId INT AUTO_INCREMENT PRIMARY KEY, Tags BLOB)");
|
||||||
|
|
||||||
list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, TagId INT,"
|
list.add("CREATE TABLE IF NOT EXISTS BatchTable (BatchId INT, EntryNum INT, SerialNum INT, Data BLOB,"
|
||||||
+ " INDEX(SignerId(32), BatchId), CONSTRAINT FOREIGN KEY (TagId) REFERENCES TagTable(TagId))");
|
+ " CONSTRAINT UNIQUE(BatchId, SerialNum),"
|
||||||
|
+ " CONSTRAINT FOREIGN KEY (BatchId) REFERENCES BatchTagTable(BatchId) ON DELETE CASCADE)");
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
return list;
|
||||||
public List<String> getSchemaDeletionCommands() {
|
}
|
||||||
List<String> list = new LinkedList<String>();
|
|
||||||
|
@Override
|
||||||
list.add("DROP TABLE IF EXISTS BatchTagTable");
|
public List<String> getSchemaDeletionCommands() {
|
||||||
list.add("DROP TABLE IF EXISTS BatchTable");
|
List<String> list = new LinkedList<String>();
|
||||||
list.add("DROP TABLE IF EXISTS MsgTagTable");
|
|
||||||
list.add("DROP TABLE IF EXISTS SignatureTable");
|
list.add("DROP TABLE IF EXISTS BatchTable");
|
||||||
list.add("DROP TABLE IF EXISTS TagTable");
|
list.add("DROP TABLE IF EXISTS BatchTagTable");
|
||||||
list.add("DROP TABLE IF EXISTS MsgTable");
|
list.add("DROP TABLE IF EXISTS MsgTagTable");
|
||||||
|
list.add("DROP TABLE IF EXISTS SignatureTable");
|
||||||
return list;
|
list.add("DROP TABLE IF EXISTS TagTable");
|
||||||
}
|
list.add("DROP TABLE IF EXISTS MsgTable");
|
||||||
}
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,233 +1,237 @@
|
||||||
package meerkat.bulletinboard.sqlserver;
|
package meerkat.bulletinboard.sqlserver;
|
||||||
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import org.sqlite.SQLiteDataSource;
|
import org.apache.commons.dbcp2.BasicDataSource;
|
||||||
|
import org.sqlite.SQLiteDataSource;
|
||||||
import javax.sql.DataSource;
|
|
||||||
import java.text.MessageFormat;
|
import javax.sql.DataSource;
|
||||||
import java.util.LinkedList;
|
import java.text.MessageFormat;
|
||||||
import java.util.List;
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
/**
|
|
||||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
/**
|
||||||
*/
|
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||||
|
*/
|
||||||
public class SQLiteQueryProvider implements BulletinBoardSQLServer.SQLQueryProvider {
|
|
||||||
|
public class SQLiteQueryProvider implements BulletinBoardSQLServer.SQLQueryProvider {
|
||||||
String dbName;
|
|
||||||
|
String dbName;
|
||||||
public SQLiteQueryProvider(String dbName) {
|
|
||||||
this.dbName = dbName;
|
public SQLiteQueryProvider(String dbName) {
|
||||||
}
|
this.dbName = dbName;
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public String getSQLString(QueryType queryType) throws IllegalArgumentException{
|
@Override
|
||||||
|
public String getSQLString(QueryType queryType) throws IllegalArgumentException{
|
||||||
switch(queryType) {
|
|
||||||
case ADD_SIGNATURE:
|
switch(queryType) {
|
||||||
return "INSERT OR IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (:EntryNum,:SignerId,:Signature)";
|
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)"
|
case CONNECT_TAG:
|
||||||
+ " SELECT TagTable.TagId, :EntryNum AS EntryNum FROM TagTable WHERE Tag = :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_MSG_ID:
|
||||||
|
return "SELECT EntryNum From MsgTable WHERE MsgId = :MsgId";
|
||||||
case FIND_TAG_ID:
|
|
||||||
return MessageFormat.format(
|
case FIND_TAG_ID:
|
||||||
"SELECT TagId FROM TagTable WHERE Tag = :{0}",
|
return MessageFormat.format(
|
||||||
QueryType.FIND_TAG_ID.getParamName(0));
|
"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 GET_MESSAGES:
|
||||||
|
return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
|
||||||
case COUNT_MESSAGES:
|
|
||||||
return "SELECT COUNT(MsgTable.EntryNum) 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_MESSAGE_STUBS:
|
||||||
|
return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable";
|
||||||
case GET_SIGNATURES:
|
|
||||||
return "SELECT Signature FROM SignatureTable WHERE EntryNum = :EntryNum";
|
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_MSG:
|
||||||
|
return "INSERT INTO MsgTable (MsgId, Msg) VALUES(:MsgId,:Msg)";
|
||||||
case INSERT_NEW_TAG:
|
|
||||||
return "INSERT OR IGNORE INTO TagTable(Tag) VALUES (:Tag)";
|
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_LAST_MESSAGE_ENTRY:
|
||||||
|
return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable";
|
||||||
case GET_BATCH_MESSAGE_ENTRY:
|
|
||||||
return MessageFormat.format(
|
case GET_BATCH_MESSAGE_DATA_BY_MSG_ID:
|
||||||
"SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"
|
return MessageFormat.format(
|
||||||
+ " INNER JOIN SignatureTable ON MsgTable.EntryNum = SignatureTable.EntryNum"
|
"SELECT Data FROM BatchTable"
|
||||||
+ " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum"
|
+ " INNER JOIN MsgTable ON MsgTable.EntryNum = BatchTable.EntryNum"
|
||||||
+ " INNER JOIN TagTable ON MsgTagTable.TagId = TagTable.TagId"
|
+ " WHERE MsgTable.MsgId = :{0} AND BatchTable.SerialNum >= :{1}"
|
||||||
+ " WHERE SignatureTable.SignerId = :{0}"
|
+ " ORDER BY BatchTable.SerialNum ASC",
|
||||||
+ " AND TagTable.Tag = :{1}",
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(0),
|
||||||
QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0),
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_MSG_ID.getParamName(1));
|
||||||
QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1));
|
|
||||||
|
case GET_BATCH_MESSAGE_DATA_BY_BATCH_ID:
|
||||||
case GET_BATCH_MESSAGE_DATA:
|
return MessageFormat.format(
|
||||||
return MessageFormat.format(
|
"SELECT Data FROM BatchTable"
|
||||||
"SELECT Data FROM BatchTable"
|
+ " WHERE BatchId = :{0} AND SerialNum >= :{1}"
|
||||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}"
|
+ " ORDER BY BatchTable.SerialNum ASC",
|
||||||
+ " ORDER BY SerialNum ASC",
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(0),
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_BATCH_ID.getParamName(1));
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),
|
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2));
|
case INSERT_BATCH_DATA:
|
||||||
|
return MessageFormat.format(
|
||||||
case INSERT_BATCH_DATA:
|
"INSERT INTO BatchTable (BatchId, SerialNum, Data) VALUES (:{0}, :{1}, :{2})",
|
||||||
return MessageFormat.format(
|
QueryType.INSERT_BATCH_DATA.getParamName(0),
|
||||||
"INSERT INTO BatchTable (SignerId, BatchId, SerialNum, Data)"
|
QueryType.INSERT_BATCH_DATA.getParamName(1),
|
||||||
+ " VALUES (:{0}, :{1}, :{2}, :{3})",
|
QueryType.INSERT_BATCH_DATA.getParamName(2));
|
||||||
QueryType.INSERT_BATCH_DATA.getParamName(0),
|
|
||||||
QueryType.INSERT_BATCH_DATA.getParamName(1),
|
case CHECK_BATCH_LENGTH:
|
||||||
QueryType.INSERT_BATCH_DATA.getParamName(2),
|
return MessageFormat.format(
|
||||||
QueryType.INSERT_BATCH_DATA.getParamName(3));
|
"SELECT COUNT(Data) AS BatchLength FROM BatchTable WHERE BatchId = :{0}",
|
||||||
|
QueryType.CHECK_BATCH_LENGTH.getParamName(0));
|
||||||
case CHECK_BATCH_LENGTH:
|
|
||||||
return MessageFormat.format(
|
case CHECK_BATCH_OPEN:
|
||||||
"SELECT COUNT(Data) AS BatchLength FROM BatchTable"
|
return MessageFormat.format(
|
||||||
+ " WHERE SignerId = :{0} AND BatchId = :{1}",
|
"SELECT COUNT(BatchId) AS batchCount FROM BatchTagTable WHERE BatchId = :{0}",
|
||||||
QueryType.CHECK_BATCH_LENGTH.getParamName(0),
|
QueryType.CHECK_BATCH_OPEN.getParamName(0));
|
||||||
QueryType.CHECK_BATCH_LENGTH.getParamName(1));
|
|
||||||
|
case STORE_BATCH_TAGS:
|
||||||
case CONNECT_BATCH_TAG:
|
return MessageFormat.format(
|
||||||
return MessageFormat.format(
|
"INSERT INTO BatchTagTable (Tags) VALUES (:{0})",
|
||||||
"INSERT INTO BatchTagTable (SignerId, BatchId, TagId) SELECT :{0}, :{1}, TagId FROM TagTable"
|
QueryType.STORE_BATCH_TAGS.getParamName(0));
|
||||||
+ " WHERE Tag = :{2}",
|
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(0),
|
case GET_BATCH_TAGS:
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(1),
|
return MessageFormat.format(
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(2));
|
"SELECT Tags FROM BatchTagTable WHERE BatchId = :{0}",
|
||||||
|
QueryType.GET_BATCH_TAGS.getParamName(0));
|
||||||
case GET_BATCH_TAGS:
|
|
||||||
return MessageFormat.format(
|
case ADD_ENTRY_NUM_TO_BATCH:
|
||||||
"SELECT Tag FROM TagTable INNER JOIN BatchTagTable ON TagTable.TagId = BatchTagTable.TagId"
|
return MessageFormat.format(
|
||||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} ORDER BY Tag ASC",
|
"UPDATE BatchTable SET EntryNum = :{1} WHERE BatchId = :{0}",
|
||||||
QueryType.GET_BATCH_TAGS.getParamName(0),
|
QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(0),
|
||||||
QueryType.GET_BATCH_TAGS.getParamName(1));
|
QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1));
|
||||||
|
|
||||||
case REMOVE_BATCH_TAGS:
|
default:
|
||||||
return MessageFormat.format(
|
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
||||||
"DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}",
|
}
|
||||||
QueryType.REMOVE_BATCH_TAGS.getParamName(0),
|
|
||||||
QueryType.REMOVE_BATCH_TAGS.getParamName(1));
|
}
|
||||||
|
|
||||||
default:
|
@Override
|
||||||
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException {
|
||||||
}
|
|
||||||
|
String serialString = Integer.toString(serialNum);
|
||||||
}
|
|
||||||
|
switch(filterType) {
|
||||||
@Override
|
case EXACT_ENTRY:
|
||||||
public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException {
|
return "MsgTable.EntryNum = :EntryNum" + serialString;
|
||||||
|
|
||||||
String serialString = Integer.toString(serialNum);
|
case MAX_ENTRY:
|
||||||
|
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
||||||
switch(filterType) {
|
|
||||||
case EXACT_ENTRY:
|
case MIN_ENTRY:
|
||||||
return "MsgTable.EntryNum = :EntryNum" + serialString;
|
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
||||||
|
|
||||||
case MAX_ENTRY:
|
case MAX_MESSAGES:
|
||||||
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
return "LIMIT = :Limit" + serialString;
|
||||||
|
|
||||||
case MIN_ENTRY:
|
case MSG_ID:
|
||||||
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
return "MsgTable.MsgId = :MsgId" + serialString;
|
||||||
|
|
||||||
case MAX_MESSAGES:
|
case SIGNER_ID:
|
||||||
return "LIMIT = :Limit" + serialString;
|
return "EXISTS (SELECT 1 FROM SignatureTable"
|
||||||
|
+ " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)";
|
||||||
case MSG_ID:
|
|
||||||
return "MsgTable.MsgId = :MsgId" + serialString;
|
case TAG:
|
||||||
|
return "EXISTS (SELECT 1 FROM TagTable"
|
||||||
case SIGNER_ID:
|
+ " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId"
|
||||||
return "EXISTS (SELECT 1 FROM SignatureTable"
|
+ " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)";
|
||||||
+ " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)";
|
|
||||||
|
case BEFORE_TIME:
|
||||||
case TAG:
|
return "MsgTable.ExactTime <= :TimeStamp";
|
||||||
return "EXISTS (SELECT 1 FROM TagTable"
|
|
||||||
+ " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId"
|
case AFTER_TIME:
|
||||||
+ " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)";
|
return "MsgTable.ExactTime >= :TimeStamp";
|
||||||
|
|
||||||
case BEFORE_TIME:
|
default:
|
||||||
return "MsgTable.ExactTime <= :TimeStamp";
|
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||||
|
}
|
||||||
case AFTER_TIME:
|
|
||||||
return "MsgTable.ExactTime >= :TimeStamp";
|
}
|
||||||
|
|
||||||
default:
|
@Override
|
||||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException {
|
||||||
}
|
|
||||||
|
switch(filterType) {
|
||||||
}
|
case EXACT_ENTRY: // Go through
|
||||||
|
case MAX_ENTRY: // Go through
|
||||||
@Override
|
case MIN_ENTRY: // Go through
|
||||||
public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException {
|
case MAX_MESSAGES:
|
||||||
|
return "INTEGER";
|
||||||
switch(filterType) {
|
|
||||||
case EXACT_ENTRY: // Go through
|
case MSG_ID: // Go through
|
||||||
case MAX_ENTRY: // Go through
|
case SIGNER_ID:
|
||||||
case MIN_ENTRY: // Go through
|
return "BLOB";
|
||||||
case MAX_MESSAGES:
|
|
||||||
return "INTEGER";
|
case TAG:
|
||||||
|
return "VARCHAR";
|
||||||
case MSG_ID: // Go through
|
|
||||||
case SIGNER_ID:
|
default:
|
||||||
return "BLOB";
|
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||||
|
}
|
||||||
case TAG:
|
|
||||||
return "VARCHAR";
|
}
|
||||||
|
|
||||||
default:
|
@Override
|
||||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
public DataSource getDataSource() {
|
||||||
}
|
|
||||||
|
BasicDataSource dataSource = new BasicDataSource();
|
||||||
}
|
dataSource.setDriverClassName("org.sqlite.JDBC");
|
||||||
|
dataSource.setUrl("jdbc:sqlite:" + dbName);
|
||||||
@Override
|
|
||||||
public DataSource getDataSource() {
|
return dataSource;
|
||||||
|
}
|
||||||
SQLiteDataSource dataSource = new SQLiteDataSource();
|
|
||||||
dataSource.setUrl("jdbc:sqlite:" + dbName);
|
|
||||||
|
@Override
|
||||||
return dataSource;
|
public List<String> getSchemaCreationCommands() {
|
||||||
}
|
List<String> list = new LinkedList<String>();
|
||||||
|
|
||||||
|
list.add("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INTEGER PRIMARY KEY, MsgId BLOB UNIQUE, Msg BLOB)");
|
||||||
@Override
|
|
||||||
public List<String> getSchemaCreationCommands() {
|
list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INTEGER PRIMARY KEY, Tag varchar(50) UNIQUE)");
|
||||||
List<String> list = new LinkedList<String>();
|
list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum BLOB, TagId INTEGER,"
|
||||||
|
+ " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE,"
|
||||||
list.add("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INTEGER PRIMARY KEY, MsgId BLOB UNIQUE, Msg BLOB)");
|
+ " FOREIGN KEY (TagId) REFERENCES TagTable(TagId) ON DELETE CASCADE,"
|
||||||
|
+ " UNIQUE (EntryNum, TagID))");
|
||||||
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)"
|
list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INTEGER, SignerId BLOB, Signature BLOB,"
|
||||||
+ " REFERENCES MsgTable(EntryNum), FOREIGN KEY (TagId) REFERENCES TagTable(TagId), UNIQUE (EntryNum, TagID))");
|
+ " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum) ON DELETE CASCADE,"
|
||||||
|
+ " UNIQUE(SignerId, EntryNum))");
|
||||||
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)");
|
||||||
|
|
||||||
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)");
|
||||||
|
|
||||||
return list;
|
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)");
|
||||||
@Override
|
|
||||||
public List<String> getSchemaDeletionCommands() {
|
return list;
|
||||||
List<String> list = new LinkedList<String>();
|
}
|
||||||
|
|
||||||
list.add("DROP TABLE IF EXISTS MsgTagTable");
|
@Override
|
||||||
|
public List<String> getSchemaDeletionCommands() {
|
||||||
list.add("DROP INDEX IF EXISTS SignerIndex");
|
List<String> list = new LinkedList<String>();
|
||||||
list.add("DROP TABLE IF EXISTS SignatureTable");
|
|
||||||
|
list.add("DROP TABLE IF EXISTS MsgTagTable");
|
||||||
list.add("DROP TABLE IF EXISTS TagTable");
|
|
||||||
list.add("DROP TABLE IF EXISTS MsgTable");
|
list.add("DROP INDEX IF EXISTS SignerIndex");
|
||||||
|
list.add("DROP TABLE IF EXISTS SignatureTable");
|
||||||
return list;
|
|
||||||
}
|
list.add("DROP TABLE IF EXISTS TagTable");
|
||||||
}
|
list.add("DROP TABLE IF EXISTS MsgTable");
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
package meerkat.bulletinboard.sqlserver.mappers;
|
package meerkat.bulletinboard.sqlserver.mappers;
|
||||||
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
|
||||||
import meerkat.comm.MessageOutputStream;
|
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.RowCallbackHandler;
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
|
@ -15,16 +13,16 @@ import java.sql.SQLException;
|
||||||
*/
|
*/
|
||||||
public class BatchDataCallbackHandler implements RowCallbackHandler {
|
public class BatchDataCallbackHandler implements RowCallbackHandler {
|
||||||
|
|
||||||
private final MessageOutputStream<BatchData> out;
|
private final MessageOutputStream<BatchChunk> out;
|
||||||
|
|
||||||
public BatchDataCallbackHandler(MessageOutputStream<BatchData> out) {
|
public BatchDataCallbackHandler(MessageOutputStream<BatchChunk> out) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processRow(ResultSet rs) throws SQLException {
|
public void processRow(ResultSet rs) throws SQLException {
|
||||||
try {
|
try {
|
||||||
out.writeMessage(BatchData.parseFrom(rs.getBytes(1)));
|
out.writeMessage(BatchChunk.parseFrom(rs.getBytes(1)));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
//TODO: Log
|
//TODO: Log
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<BatchData> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BatchData mapRow(ResultSet rs, int rowNum) throws SQLException {
|
|
||||||
|
|
||||||
try {
|
|
||||||
return BatchData.parseFrom(rs.getBytes(1));
|
|
||||||
} catch (InvalidProtocolBufferException e) {
|
|
||||||
return BatchData.getDefaultInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<BeginBatchMessage> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BeginBatchMessage mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||||
|
try {
|
||||||
|
return BeginBatchMessage.newBuilder().mergeFrom(rs.getBytes(1)).build();
|
||||||
|
} catch (InvalidProtocolBufferException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,18 +1,18 @@
|
||||||
package meerkat.bulletinboard.sqlserver.mappers;
|
package meerkat.bulletinboard.sqlserver.mappers;
|
||||||
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.MessageID;
|
import meerkat.protobuf.BulletinBoardAPI.MessageID;
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 11-Dec-15.
|
* Created by Arbel Deutsch Peled on 11-Dec-15.
|
||||||
*/
|
*/
|
||||||
public class LongMapper implements RowMapper<Long> {
|
public class LongMapper implements RowMapper<Long> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long mapRow(ResultSet rs, int rowNum) throws SQLException {
|
public Long mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||||
return rs.getLong(1);
|
return rs.getLong(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,32 @@
|
||||||
package meerkat.bulletinboard.sqlserver.mappers;
|
package meerkat.bulletinboard.sqlserver.mappers;
|
||||||
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
|
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 11-Dec-15.
|
* Created by Arbel Deutsch Peled on 11-Dec-15.
|
||||||
*/
|
*/
|
||||||
public class MessageMapper implements RowMapper<BulletinBoardMessage.Builder> {
|
public class MessageMapper implements RowMapper<BulletinBoardMessage.Builder> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BulletinBoardMessage.Builder mapRow(ResultSet rs, int rowNum) throws SQLException {
|
public BulletinBoardMessage.Builder mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||||
|
|
||||||
BulletinBoardMessage.Builder builder = BulletinBoardMessage.newBuilder();
|
BulletinBoardMessage.Builder builder = BulletinBoardMessage.newBuilder();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
builder.setEntryNum(rs.getLong(1))
|
builder.setEntryNum(rs.getLong(1))
|
||||||
.setMsg(UnsignedBulletinBoardMessage.parseFrom(rs.getBytes(2)));
|
.setMsg(UnsignedBulletinBoardMessage.parseFrom(rs.getBytes(2)));
|
||||||
|
|
||||||
} catch (InvalidProtocolBufferException e) {
|
} catch (InvalidProtocolBufferException e) {
|
||||||
throw new SQLException(e.getMessage(), e);
|
throw new SQLException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ public class MessageStubMapper implements RowMapper<BulletinBoardMessage> {
|
||||||
return BulletinBoardMessage.newBuilder()
|
return BulletinBoardMessage.newBuilder()
|
||||||
.setEntryNum(rs.getLong(1))
|
.setEntryNum(rs.getLong(1))
|
||||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||||
.setData(ByteString.copyFrom(rs.getBytes(2)))
|
.setMsgId(ByteString.copyFrom(rs.getBytes(2)))
|
||||||
.setTimestamp(BulletinBoardUtils.toTimestampProto(rs.getTimestamp(3)))
|
.setTimestamp(BulletinBoardUtils.toTimestampProto(rs.getTimestamp(3)))
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
package meerkat.bulletinboard.sqlserver.mappers;
|
package meerkat.bulletinboard.sqlserver.mappers;
|
||||||
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
|
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
|
||||||
import meerkat.protobuf.Crypto.Signature;
|
import meerkat.protobuf.Crypto.Signature;
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 11-Dec-15.
|
* Created by Arbel Deutsch Peled on 11-Dec-15.
|
||||||
*/
|
*/
|
||||||
public class SignatureMapper implements RowMapper<Signature> {
|
public class SignatureMapper implements RowMapper<Signature> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Signature mapRow(ResultSet rs, int rowNum) throws SQLException {
|
public Signature mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Signature.parseFrom(rs.getBytes(1));
|
return Signature.parseFrom(rs.getBytes(1));
|
||||||
} catch (InvalidProtocolBufferException e) {
|
} catch (InvalidProtocolBufferException e) {
|
||||||
throw new SQLException(e.getMessage(), e);
|
throw new SQLException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,266 +1,268 @@
|
||||||
package meerkat.bulletinboard.webapp;
|
package meerkat.bulletinboard.webapp;
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
import javax.servlet.ServletContextEvent;
|
import javax.servlet.ServletContextEvent;
|
||||||
import javax.servlet.ServletContextListener;
|
import javax.servlet.ServletContextListener;
|
||||||
import javax.ws.rs.*;
|
import javax.ws.rs.*;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.StreamingOutput;
|
import javax.ws.rs.core.StreamingOutput;
|
||||||
|
|
||||||
import com.google.protobuf.BoolValue;
|
import com.google.protobuf.BoolValue;
|
||||||
import meerkat.bulletinboard.BulletinBoardServer;
|
import com.google.protobuf.Int32Value;
|
||||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
import com.google.protobuf.Int64Value;
|
||||||
import meerkat.bulletinboard.sqlserver.H2QueryProvider;
|
import meerkat.bulletinboard.BulletinBoardServer;
|
||||||
import meerkat.bulletinboard.sqlserver.MySQLQueryProvider;
|
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
||||||
import meerkat.bulletinboard.sqlserver.SQLiteQueryProvider;
|
import meerkat.bulletinboard.sqlserver.H2QueryProvider;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.bulletinboard.sqlserver.MySQLQueryProvider;
|
||||||
import meerkat.comm.MessageOutputStream;
|
import meerkat.bulletinboard.sqlserver.SQLiteQueryProvider;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.protobuf.Comm.*;
|
import meerkat.comm.MessageOutputStream;
|
||||||
import static meerkat.bulletinboard.BulletinBoardConstants.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import static meerkat.rest.Constants.*;
|
import static meerkat.bulletinboard.BulletinBoardConstants.*;
|
||||||
|
import static meerkat.rest.Constants.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of the BulletinBoardServer which functions as a WebApp
|
* An implementation of the BulletinBoardServer which functions as a WebApp
|
||||||
*/
|
*/
|
||||||
@Path(BULLETIN_BOARD_SERVER_PATH)
|
@Path(BULLETIN_BOARD_SERVER_PATH)
|
||||||
public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextListener{
|
public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextListener{
|
||||||
|
|
||||||
private static final String BULLETIN_BOARD_ATTRIBUTE_NAME = "bulletinBoard";
|
private static final String BULLETIN_BOARD_ATTRIBUTE_NAME = "bulletinBoard";
|
||||||
|
|
||||||
@Context ServletContext servletContext;
|
@Context ServletContext servletContext;
|
||||||
|
|
||||||
BulletinBoardServer bulletinBoard;
|
BulletinBoardServer bulletinBoard;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the servlet init method.
|
* This is the servlet init method.
|
||||||
*/
|
*/
|
||||||
public void init(){
|
public void init(){
|
||||||
bulletinBoard = (BulletinBoardServer) servletContext.getAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME);
|
bulletinBoard = (BulletinBoardServer) servletContext.getAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* This is the BulletinBoard init method.
|
public void contextInitialized(ServletContextEvent servletContextEvent) {
|
||||||
*/
|
ServletContext servletContext = servletContextEvent.getServletContext();
|
||||||
@Override
|
String dbType = servletContext.getInitParameter("dbType");
|
||||||
public void init(String meerkatDB) throws CommunicationException {
|
String dbName = servletContext.getInitParameter("dbName");
|
||||||
bulletinBoard.init(meerkatDB);
|
|
||||||
}
|
if ("SQLite".equals(dbType)){
|
||||||
|
|
||||||
@Override
|
bulletinBoard = new BulletinBoardSQLServer(new SQLiteQueryProvider(dbName));
|
||||||
public void contextInitialized(ServletContextEvent servletContextEvent) {
|
|
||||||
ServletContext servletContext = servletContextEvent.getServletContext();
|
} else if ("H2".equals(dbType)) {
|
||||||
String dbType = servletContext.getInitParameter("dbType");
|
|
||||||
String dbName = servletContext.getInitParameter("dbName");
|
bulletinBoard = new BulletinBoardSQLServer(new H2QueryProvider(dbName));
|
||||||
|
|
||||||
if ("SQLite".equals(dbType)){
|
} else if ("MySQL".equals(dbType)) {
|
||||||
|
|
||||||
bulletinBoard = new BulletinBoardSQLServer(new SQLiteQueryProvider(dbName));
|
String dbAddress = servletContext.getInitParameter("dbAddress");
|
||||||
|
int dbPort = Integer.parseInt(servletContext.getInitParameter("dbPort"));
|
||||||
} else if ("H2".equals(dbType)) {
|
String username = servletContext.getInitParameter("username");
|
||||||
|
String password = servletContext.getInitParameter("password");
|
||||||
bulletinBoard = new BulletinBoardSQLServer(new H2QueryProvider(dbName));
|
|
||||||
|
bulletinBoard = new BulletinBoardSQLServer(new MySQLQueryProvider(dbAddress,dbPort,dbName,username,password));
|
||||||
} else if ("MySQL".equals(dbType)) {
|
}
|
||||||
|
|
||||||
String dbAddress = servletContext.getInitParameter("dbAddress");
|
try {
|
||||||
int dbPort = Integer.parseInt(servletContext.getInitParameter("dbPort"));
|
bulletinBoard.init();
|
||||||
String username = servletContext.getInitParameter("username");
|
servletContext.setAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME, bulletinBoard);
|
||||||
String password = servletContext.getInitParameter("password");
|
} catch (CommunicationException e) {
|
||||||
|
System.err.println(e.getMessage());
|
||||||
bulletinBoard = new BulletinBoardSQLServer(new MySQLQueryProvider(dbAddress,dbPort,dbName,username,password));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
@Path(POST_MESSAGE_PATH)
|
||||||
init(dbName);
|
@POST
|
||||||
servletContext.setAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME, bulletinBoard);
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
} catch (CommunicationException e) {
|
@Produces(MEDIATYPE_PROTOBUF)
|
||||||
System.err.println(e.getMessage());
|
@Override
|
||||||
}
|
public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||||
}
|
init();
|
||||||
|
return bulletinBoard.postMessage(msg);
|
||||||
@Path(POST_MESSAGE_PATH)
|
}
|
||||||
@POST
|
|
||||||
@Consumes(MEDIATYPE_PROTOBUF)
|
@Override
|
||||||
@Produces(MEDIATYPE_PROTOBUF)
|
public void readMessages(MessageFilterList filterList, MessageOutputStream<BulletinBoardMessage> out) throws CommunicationException {
|
||||||
@Override
|
init();
|
||||||
public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
bulletinBoard.readMessages(filterList, out);
|
||||||
init();
|
}
|
||||||
return bulletinBoard.postMessage(msg);
|
|
||||||
}
|
@Path(COUNT_MESSAGES_PATH)
|
||||||
|
@POST
|
||||||
@Override
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
public void readMessages(MessageFilterList filterList, MessageOutputStream<BulletinBoardMessage> out) throws CommunicationException {
|
@Produces(MEDIATYPE_PROTOBUF)
|
||||||
init();
|
@Override
|
||||||
bulletinBoard.readMessages(filterList, out);
|
public Int32Value getMessageCount(MessageFilterList filterList) throws CommunicationException {
|
||||||
}
|
init();
|
||||||
|
return bulletinBoard.getMessageCount(filterList);
|
||||||
|
}
|
||||||
@Path(READ_MESSAGES_PATH)
|
|
||||||
@POST
|
|
||||||
@Consumes(MEDIATYPE_PROTOBUF)
|
@Path(READ_MESSAGES_PATH)
|
||||||
/**
|
@POST
|
||||||
* Wrapper for the readMessages method which streams the output into the response
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
*/
|
/**
|
||||||
public StreamingOutput readMessages(final MessageFilterList filterList) {
|
* Wrapper for the readMessages method which streams the output into the response
|
||||||
|
*/
|
||||||
return new StreamingOutput() {
|
public StreamingOutput readMessages(final MessageFilterList filterList) {
|
||||||
|
|
||||||
@Override
|
return new StreamingOutput() {
|
||||||
public void write(OutputStream output) throws IOException, WebApplicationException {
|
|
||||||
MessageOutputStream<BulletinBoardMessage> out = new MessageOutputStream<>(output);
|
@Override
|
||||||
|
public void write(OutputStream output) throws IOException, WebApplicationException {
|
||||||
try {
|
MessageOutputStream<BulletinBoardMessage> out = new MessageOutputStream<>(output);
|
||||||
init();
|
|
||||||
bulletinBoard.readMessages(filterList, out);
|
try {
|
||||||
} catch (CommunicationException e) {
|
init();
|
||||||
//TODO: Log
|
bulletinBoard.readMessages(filterList, out);
|
||||||
out.writeMessage(null);
|
} catch (CommunicationException e) {
|
||||||
}
|
//TODO: Log
|
||||||
}
|
out.writeMessage(null);
|
||||||
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
@Path(BEGIN_BATCH_PATH)
|
}
|
||||||
@POST
|
|
||||||
@Consumes(MEDIATYPE_PROTOBUF)
|
@Path(BEGIN_BATCH_PATH)
|
||||||
@Produces(MEDIATYPE_PROTOBUF)
|
@POST
|
||||||
@Override
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
public BoolValue beginBatch(BeginBatchMessage message) {
|
@Produces(MEDIATYPE_PROTOBUF)
|
||||||
try {
|
@Override
|
||||||
init();
|
public Int64Value beginBatch(BeginBatchMessage message) {
|
||||||
return bulletinBoard.beginBatch(message);
|
try {
|
||||||
} catch (CommunicationException e) {
|
init();
|
||||||
System.err.println(e.getMessage());
|
return bulletinBoard.beginBatch(message);
|
||||||
return null;
|
} catch (CommunicationException e) {
|
||||||
}
|
System.err.println(e.getMessage());
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
@Path(POST_BATCH_PATH)
|
}
|
||||||
@POST
|
|
||||||
@Consumes(MEDIATYPE_PROTOBUF)
|
@Path(POST_BATCH_PATH)
|
||||||
@Produces(MEDIATYPE_PROTOBUF)
|
@POST
|
||||||
@Override
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
public BoolValue postBatchMessage(BatchMessage batchMessage) {
|
@Produces(MEDIATYPE_PROTOBUF)
|
||||||
try {
|
@Override
|
||||||
init();
|
public BoolValue postBatchMessage(BatchMessage batchMessage) {
|
||||||
return bulletinBoard.postBatchMessage(batchMessage);
|
try {
|
||||||
} catch (CommunicationException e) {
|
init();
|
||||||
System.err.println(e.getMessage());
|
return bulletinBoard.postBatchMessage(batchMessage);
|
||||||
return null;
|
} catch (CommunicationException e) {
|
||||||
}
|
System.err.println(e.getMessage());
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
@Path(CLOSE_BATCH_PATH)
|
}
|
||||||
@POST
|
|
||||||
@Consumes(MEDIATYPE_PROTOBUF)
|
@Path(CLOSE_BATCH_PATH)
|
||||||
@Produces(MEDIATYPE_PROTOBUF)
|
@POST
|
||||||
@Override
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
public BoolValue closeBatchMessage(CloseBatchMessage message) {
|
@Produces(MEDIATYPE_PROTOBUF)
|
||||||
try {
|
@Override
|
||||||
init();
|
public BoolValue closeBatch(CloseBatchMessage message) {
|
||||||
return bulletinBoard.closeBatchMessage(message);
|
try {
|
||||||
} catch (CommunicationException e) {
|
init();
|
||||||
System.err.println(e.getMessage());
|
return bulletinBoard.closeBatch(message);
|
||||||
return null;
|
} catch (CommunicationException e) {
|
||||||
}
|
System.err.println(e.getMessage());
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public void readBatch(BatchSpecificationMessage message, MessageOutputStream<BatchData> out) {
|
|
||||||
try {
|
@Override
|
||||||
init();
|
public void readBatch(BatchQuery batchQuery, MessageOutputStream<BatchChunk> out) throws CommunicationException, IllegalArgumentException {
|
||||||
bulletinBoard.readBatch(message, out);
|
try {
|
||||||
} catch (CommunicationException | IllegalArgumentException e) {
|
init();
|
||||||
System.err.println(e.getMessage());
|
bulletinBoard.readBatch(batchQuery, out);
|
||||||
}
|
} catch (CommunicationException | IllegalArgumentException e) {
|
||||||
}
|
System.err.println(e.getMessage());
|
||||||
|
}
|
||||||
@Path(GENERATE_SYNC_QUERY_PATH)
|
}
|
||||||
@POST
|
|
||||||
@Consumes(MEDIATYPE_PROTOBUF)
|
@Path(GENERATE_SYNC_QUERY_PATH)
|
||||||
@Produces(MEDIATYPE_PROTOBUF)
|
@POST
|
||||||
@Override
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException {
|
@Produces(MEDIATYPE_PROTOBUF)
|
||||||
try {
|
@Override
|
||||||
init();
|
public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException {
|
||||||
return bulletinBoard.generateSyncQuery(generateSyncQueryParams);
|
try {
|
||||||
} catch (CommunicationException | IllegalArgumentException e) {
|
init();
|
||||||
System.err.println(e.getMessage());
|
return bulletinBoard.generateSyncQuery(generateSyncQueryParams);
|
||||||
return null;
|
} catch (CommunicationException | IllegalArgumentException e) {
|
||||||
}
|
System.err.println(e.getMessage());
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
@Path(READ_BATCH_PATH)
|
}
|
||||||
@POST
|
|
||||||
@Consumes(MEDIATYPE_PROTOBUF)
|
@Path(READ_BATCH_PATH)
|
||||||
/**
|
@POST
|
||||||
* Wrapper for the readBatch method which streams the output into the response
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
*/
|
/**
|
||||||
public StreamingOutput readBatch(final BatchSpecificationMessage message) {
|
* Wrapper for the readBatch method which streams the output into the response
|
||||||
|
*/
|
||||||
return new StreamingOutput() {
|
public StreamingOutput readBatch(final BatchQuery batchQuery) {
|
||||||
|
|
||||||
@Override
|
return new StreamingOutput() {
|
||||||
public void write(OutputStream output) throws IOException, WebApplicationException {
|
|
||||||
MessageOutputStream<BatchData> out = new MessageOutputStream<>(output);
|
@Override
|
||||||
|
public void write(OutputStream output) throws IOException, WebApplicationException {
|
||||||
try {
|
MessageOutputStream<BatchChunk> out = new MessageOutputStream<>(output);
|
||||||
init();
|
|
||||||
bulletinBoard.readBatch(message, out);
|
try {
|
||||||
} catch (CommunicationException e) {
|
init();
|
||||||
//TODO: Log
|
bulletinBoard.readBatch(batchQuery, out);
|
||||||
out.writeMessage(null);
|
} catch (CommunicationException e) {
|
||||||
}
|
//TODO: Log
|
||||||
}
|
out.writeMessage(null);
|
||||||
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
@Path(SYNC_QUERY_PATH)
|
}
|
||||||
@POST
|
|
||||||
@Consumes(MEDIATYPE_PROTOBUF)
|
@Path(SYNC_QUERY_PATH)
|
||||||
@Produces(MEDIATYPE_PROTOBUF)
|
@POST
|
||||||
@Override
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
public SyncQueryResponse querySync(SyncQuery syncQuery) throws CommunicationException {
|
@Produces(MEDIATYPE_PROTOBUF)
|
||||||
try{
|
@Override
|
||||||
init();
|
public SyncQueryResponse querySync(SyncQuery syncQuery) throws CommunicationException {
|
||||||
return bulletinBoard.querySync(syncQuery);
|
try{
|
||||||
} catch (CommunicationException | IllegalArgumentException e) {
|
init();
|
||||||
System.err.println(e.getMessage());
|
return bulletinBoard.querySync(syncQuery);
|
||||||
return null;
|
} catch (CommunicationException | IllegalArgumentException e) {
|
||||||
}
|
System.err.println(e.getMessage());
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
@Override
|
}
|
||||||
public void close(){
|
|
||||||
try {
|
@Override
|
||||||
bulletinBoard.close();
|
public void close(){
|
||||||
} catch (CommunicationException e) {
|
try {
|
||||||
System.err.println(e.getMessage());
|
bulletinBoard.close();
|
||||||
}
|
} catch (CommunicationException e) {
|
||||||
}
|
System.err.println(e.getMessage());
|
||||||
|
}
|
||||||
@GET
|
}
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
public String test() {
|
@GET
|
||||||
return "This BulletinBoard is up and running!\n Please consult the API documents to perform queries.";
|
@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();
|
@Override
|
||||||
bulletinBoard = (BulletinBoardServer) servletContext.getAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME);
|
public void contextDestroyed(ServletContextEvent servletContextEvent) {
|
||||||
close();
|
ServletContext servletContext = servletContextEvent.getServletContext();
|
||||||
}
|
bulletinBoard = (BulletinBoardServer) servletContext.getAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME);
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,50 +1,50 @@
|
||||||
package meerkat.bulletinboard.webapp;
|
package meerkat.bulletinboard.webapp;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.Message;
|
import com.google.protobuf.Message;
|
||||||
import meerkat.bulletinboard.service.HelloProtoBuf;
|
import meerkat.bulletinboard.service.HelloProtoBuf;
|
||||||
import meerkat.protobuf.Crypto.*;
|
import meerkat.protobuf.Crypto.*;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.rest.Constants;
|
import meerkat.rest.Constants;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
|
||||||
@Path("/proto")
|
@Path("/proto")
|
||||||
public class HelloProtoWebApp {
|
public class HelloProtoWebApp {
|
||||||
private HelloProtoBuf helloProtoBuf;
|
private HelloProtoBuf helloProtoBuf;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
helloProtoBuf = new HelloProtoBuf();
|
helloProtoBuf = new HelloProtoBuf();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(Constants.MEDIATYPE_PROTOBUF)
|
@Produces(Constants.MEDIATYPE_PROTOBUF)
|
||||||
public Message hello() {
|
public Message hello() {
|
||||||
byte[] b1 = { (byte) 1, (byte) 2, (byte) 3, (byte) 4 };
|
byte[] b1 = { (byte) 1, (byte) 2, (byte) 3, (byte) 4 };
|
||||||
byte[] b2 = { (byte) 11, (byte) 12, (byte) 13, (byte) 14 };
|
byte[] b2 = { (byte) 11, (byte) 12, (byte) 13, (byte) 14 };
|
||||||
byte[] b3 = {(byte) 21, (byte)22, (byte) 23, (byte) 24};
|
byte[] b3 = {(byte) 21, (byte)22, (byte) 23, (byte) 24};
|
||||||
|
|
||||||
Message msg;
|
Message msg;
|
||||||
|
|
||||||
if (helloProtoBuf != null) {
|
if (helloProtoBuf != null) {
|
||||||
msg = helloProtoBuf.sayHello();
|
msg = helloProtoBuf.sayHello();
|
||||||
} else {
|
} else {
|
||||||
msg = BulletinBoardMessage.newBuilder()
|
msg = BulletinBoardMessage.newBuilder()
|
||||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||||
.addTag("Signature")
|
.addTag("Signature")
|
||||||
.addTag("Trustee")
|
.addTag("Trustee")
|
||||||
.setData(ByteString.copyFrom(b1)).build())
|
.setData(ByteString.copyFrom(b1)).build())
|
||||||
.addSig(Signature.newBuilder()
|
.addSig(Signature.newBuilder()
|
||||||
.setType(SignatureType.DSA)
|
.setType(SignatureType.DSA)
|
||||||
.setData(ByteString.copyFrom(b2))
|
.setData(ByteString.copyFrom(b2))
|
||||||
.setSignerId(ByteString.copyFrom(b3)).build())
|
.setSignerId(ByteString.copyFrom(b3)).build())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package meerkat;
|
|
||||||
|
|
||||||
option java_package = "meerkat.protobuf";
|
|
||||||
|
|
||||||
message Boolean {
|
|
||||||
bool value = 1;
|
|
||||||
}
|
|
|
@ -1,12 +1,12 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
|
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
|
||||||
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||||
<Call name="setAttribute">
|
<Call name="setAttribute">
|
||||||
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
|
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
|
||||||
<Arg>none</Arg>
|
<Arg>none</Arg>
|
||||||
</Call>
|
</Call>
|
||||||
<Call name="setAttribute">
|
<Call name="setAttribute">
|
||||||
<Arg>org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern</Arg>
|
<Arg>org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern</Arg>
|
||||||
<Arg>none</Arg>
|
<Arg>none</Arg>
|
||||||
</Call>
|
</Call>
|
||||||
</Configure>
|
</Configure>
|
|
@ -1,38 +1,38 @@
|
||||||
<web-app>
|
<web-app>
|
||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>Jersey Hello World</servlet-name>
|
<servlet-name>Jersey Hello World</servlet-name>
|
||||||
<servlet-class>
|
<servlet-class>
|
||||||
org.glassfish.jersey.servlet.ServletContainer
|
org.glassfish.jersey.servlet.ServletContainer
|
||||||
</servlet-class>
|
</servlet-class>
|
||||||
<init-param>
|
<init-param>
|
||||||
<param-name>jersey.config.server.provider.packages</param-name>
|
<param-name>jersey.config.server.provider.packages</param-name>
|
||||||
<param-value>meerkat</param-value>
|
<param-value>meerkat</param-value>
|
||||||
</init-param>
|
</init-param>
|
||||||
<load-on-startup>1</load-on-startup>
|
<load-on-startup>1</load-on-startup>
|
||||||
</servlet>
|
</servlet>
|
||||||
<servlet-mapping>
|
<servlet-mapping>
|
||||||
<servlet-name>Jersey Hello World</servlet-name>
|
<servlet-name>Jersey Hello World</servlet-name>
|
||||||
<url-pattern>/*</url-pattern>
|
<url-pattern>/*</url-pattern>
|
||||||
</servlet-mapping>
|
</servlet-mapping>
|
||||||
<context-param>
|
<context-param>
|
||||||
<param-name>dbAddress</param-name>
|
<param-name>dbAddress</param-name>
|
||||||
<param-value>localhost</param-value></context-param>
|
<param-value>localhost</param-value></context-param>
|
||||||
<context-param>
|
<context-param>
|
||||||
<param-name>dbPort</param-name>
|
<param-name>dbPort</param-name>
|
||||||
<param-value>3306</param-value></context-param>
|
<param-value>3306</param-value></context-param>
|
||||||
<context-param>
|
<context-param>
|
||||||
<param-name>dbName</param-name>
|
<param-name>dbName</param-name>
|
||||||
<param-value>meerkat</param-value></context-param>
|
<param-value>meerkat</param-value></context-param>
|
||||||
<context-param>
|
<context-param>
|
||||||
<param-name>username</param-name>
|
<param-name>username</param-name>
|
||||||
<param-value>arbel</param-value></context-param>
|
<param-value>arbel</param-value></context-param>
|
||||||
<context-param>
|
<context-param>
|
||||||
<param-name>password</param-name>
|
<param-name>password</param-name>
|
||||||
<param-value>mypass</param-value></context-param>
|
<param-value>mypass</param-value></context-param>
|
||||||
<context-param>
|
<context-param>
|
||||||
<param-name>dbType</param-name>
|
<param-name>dbType</param-name>
|
||||||
<param-value>H2</param-value></context-param>
|
<param-value>H2</param-value></context-param>
|
||||||
<listener>
|
<listener>
|
||||||
<listener-class>meerkat.bulletinboard.webapp.BulletinBoardWebApp</listener-class>
|
<listener-class>meerkat.bulletinboard.webapp.BulletinBoardWebApp</listener-class>
|
||||||
</listener>
|
</listener>
|
||||||
</web-app>
|
</web-app>
|
||||||
|
|
|
@ -1,150 +1,150 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
|
|
||||||
import com.google.protobuf.BoolValue;
|
import com.google.protobuf.BoolValue;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.TextFormat;
|
import com.google.protobuf.TextFormat;
|
||||||
|
|
||||||
import com.google.protobuf.Timestamp;
|
import com.google.protobuf.Timestamp;
|
||||||
import meerkat.comm.MessageInputStream;
|
import meerkat.comm.MessageInputStream;
|
||||||
import meerkat.protobuf.Crypto.*;
|
import meerkat.protobuf.Crypto.*;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.protobuf.Comm.*;
|
import meerkat.protobuf.Comm.*;
|
||||||
import static meerkat.bulletinboard.BulletinBoardConstants.*;
|
import static meerkat.bulletinboard.BulletinBoardConstants.*;
|
||||||
import meerkat.rest.Constants;
|
import meerkat.rest.Constants;
|
||||||
import meerkat.rest.ProtobufMessageBodyReader;
|
import meerkat.rest.ProtobufMessageBodyReader;
|
||||||
import meerkat.rest.ProtobufMessageBodyWriter;
|
import meerkat.rest.ProtobufMessageBodyWriter;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import javax.ws.rs.client.Client;
|
import javax.ws.rs.client.Client;
|
||||||
import javax.ws.rs.client.ClientBuilder;
|
import javax.ws.rs.client.ClientBuilder;
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Entity;
|
||||||
import javax.ws.rs.client.WebTarget;
|
import javax.ws.rs.client.WebTarget;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class BulletinBoardSQLServerIntegrationTest {
|
public class BulletinBoardSQLServerIntegrationTest {
|
||||||
|
|
||||||
private static String PROP_GETTY_URL = "gretty.httpBaseURI";
|
private static String PROP_GETTY_URL = "gretty.httpBaseURI";
|
||||||
private static String DEFAULT_BASE_URL = "http://localhost:8081";
|
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 BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL);
|
||||||
|
|
||||||
Client client;
|
Client client;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
System.err.println("Registering client");
|
System.err.println("Registering client");
|
||||||
client = ClientBuilder.newClient();
|
client = ClientBuilder.newClient();
|
||||||
client.register(ProtobufMessageBodyReader.class);
|
client.register(ProtobufMessageBodyReader.class);
|
||||||
client.register(ProtobufMessageBodyWriter.class);
|
client.register(ProtobufMessageBodyWriter.class);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPost() throws Exception {
|
public void testPost() throws Exception {
|
||||||
byte[] b1 = {(byte) 1, (byte) 2, (byte) 3, (byte) 4};
|
byte[] b1 = {(byte) 1, (byte) 2, (byte) 3, (byte) 4};
|
||||||
byte[] b2 = {(byte) 11, (byte) 12, (byte) 13, (byte) 14};
|
byte[] b2 = {(byte) 11, (byte) 12, (byte) 13, (byte) 14};
|
||||||
byte[] b3 = {(byte) 21, (byte) 22, (byte) 23, (byte) 24};
|
byte[] b3 = {(byte) 21, (byte) 22, (byte) 23, (byte) 24};
|
||||||
byte[] b4 = {(byte) 4, (byte) 5, (byte) 100, (byte) -50, (byte) 0};
|
byte[] b4 = {(byte) 4, (byte) 5, (byte) 100, (byte) -50, (byte) 0};
|
||||||
|
|
||||||
Timestamp t1 = Timestamp.newBuilder()
|
Timestamp t1 = Timestamp.newBuilder()
|
||||||
.setSeconds(8276482)
|
.setSeconds(8276482)
|
||||||
.setNanos(4314)
|
.setNanos(4314)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Timestamp t2 = Timestamp.newBuilder()
|
Timestamp t2 = Timestamp.newBuilder()
|
||||||
.setSeconds(987591)
|
.setSeconds(987591)
|
||||||
.setNanos(1513)
|
.setNanos(1513)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
WebTarget webTarget;
|
WebTarget webTarget;
|
||||||
Response response;
|
Response response;
|
||||||
BoolValue bool;
|
BoolValue bool;
|
||||||
|
|
||||||
BulletinBoardMessage msg;
|
BulletinBoardMessage msg;
|
||||||
|
|
||||||
MessageFilterList filterList;
|
MessageFilterList filterList;
|
||||||
List<BulletinBoardMessage> msgList;
|
List<BulletinBoardMessage> msgList;
|
||||||
|
|
||||||
// Test writing mechanism
|
// Test writing mechanism
|
||||||
|
|
||||||
System.err.println("******** Testing: " + POST_MESSAGE_PATH);
|
System.err.println("******** Testing: " + POST_MESSAGE_PATH);
|
||||||
webTarget = client.target(BASE_URL).path(BULLETIN_BOARD_SERVER_PATH).path(POST_MESSAGE_PATH);
|
webTarget = client.target(BASE_URL).path(BULLETIN_BOARD_SERVER_PATH).path(POST_MESSAGE_PATH);
|
||||||
System.err.println(webTarget.getUri());
|
System.err.println(webTarget.getUri());
|
||||||
|
|
||||||
msg = BulletinBoardMessage.newBuilder()
|
msg = BulletinBoardMessage.newBuilder()
|
||||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||||
.addTag("Signature")
|
.addTag("Signature")
|
||||||
.addTag("Trustee")
|
.addTag("Trustee")
|
||||||
.setData(ByteString.copyFrom(b1))
|
.setData(ByteString.copyFrom(b1))
|
||||||
.setTimestamp(t1)
|
.setTimestamp(t1)
|
||||||
.build())
|
.build())
|
||||||
.addSig(Signature.newBuilder()
|
.addSig(Signature.newBuilder()
|
||||||
.setType(SignatureType.DSA)
|
.setType(SignatureType.DSA)
|
||||||
.setData(ByteString.copyFrom(b2))
|
.setData(ByteString.copyFrom(b2))
|
||||||
.setSignerId(ByteString.copyFrom(b3))
|
.setSignerId(ByteString.copyFrom(b3))
|
||||||
.build())
|
.build())
|
||||||
.addSig(Signature.newBuilder()
|
.addSig(Signature.newBuilder()
|
||||||
.setType(SignatureType.ECDSA)
|
.setType(SignatureType.ECDSA)
|
||||||
.setData(ByteString.copyFrom(b3))
|
.setData(ByteString.copyFrom(b3))
|
||||||
.setSignerId(ByteString.copyFrom(b2))
|
.setSignerId(ByteString.copyFrom(b2))
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
||||||
System.err.println(response);
|
System.err.println(response);
|
||||||
bool = response.readEntity(BoolValue.class);
|
bool = response.readEntity(BoolValue.class);
|
||||||
assert bool.getValue();
|
assert bool.getValue();
|
||||||
|
|
||||||
msg = BulletinBoardMessage.newBuilder()
|
msg = BulletinBoardMessage.newBuilder()
|
||||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||||
.addTag("Vote")
|
.addTag("Vote")
|
||||||
.addTag("Trustee")
|
.addTag("Trustee")
|
||||||
.setData(ByteString.copyFrom(b4))
|
.setData(ByteString.copyFrom(b4))
|
||||||
.setTimestamp(t2)
|
.setTimestamp(t2)
|
||||||
.build())
|
.build())
|
||||||
.addSig(Signature.newBuilder()
|
.addSig(Signature.newBuilder()
|
||||||
.setType(SignatureType.ECDSA)
|
.setType(SignatureType.ECDSA)
|
||||||
.setData(ByteString.copyFrom(b4))
|
.setData(ByteString.copyFrom(b4))
|
||||||
.setSignerId(ByteString.copyFrom(b2))
|
.setSignerId(ByteString.copyFrom(b2))
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
||||||
System.err.println(response);
|
System.err.println(response);
|
||||||
bool = response.readEntity(BoolValue.class);
|
bool = response.readEntity(BoolValue.class);
|
||||||
assert bool.getValue();
|
assert bool.getValue();
|
||||||
|
|
||||||
// Test reading mechanism
|
// Test reading mechanism
|
||||||
|
|
||||||
System.err.println("******** Testing: " + READ_MESSAGES_PATH);
|
System.err.println("******** Testing: " + READ_MESSAGES_PATH);
|
||||||
webTarget = client.target(BASE_URL).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH);
|
webTarget = client.target(BASE_URL).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH);
|
||||||
filterList = MessageFilterList.newBuilder()
|
filterList = MessageFilterList.newBuilder()
|
||||||
.addFilter(
|
.addFilter(
|
||||||
MessageFilter.newBuilder()
|
MessageFilter.newBuilder()
|
||||||
.setType(FilterType.TAG)
|
.setType(FilterType.TAG)
|
||||||
.setTag("Vote")
|
.setTag("Vote")
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF), InputStream.class);
|
InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF), InputStream.class);
|
||||||
|
|
||||||
MessageInputStream<BulletinBoardMessage> inputStream =
|
MessageInputStream<BulletinBoardMessage> inputStream =
|
||||||
MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BulletinBoardMessage.class);
|
MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BulletinBoardMessage.class);
|
||||||
|
|
||||||
msgList = inputStream.asList();
|
msgList = inputStream.asList();
|
||||||
System.err.println("List size: " + msgList.size());
|
System.err.println("List size: " + msgList.size());
|
||||||
System.err.println("This is the list:");
|
System.err.println("This is the list:");
|
||||||
|
|
||||||
for (BulletinBoardMessage message : msgList) {
|
for (BulletinBoardMessage message : msgList) {
|
||||||
System.err.println(TextFormat.printToString(message));
|
System.err.println(TextFormat.printToString(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
assert msgList.size() == 1;
|
assert msgList.size() == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,165 +1,154 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
||||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider;
|
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider;
|
||||||
import meerkat.bulletinboard.sqlserver.H2QueryProvider;
|
import meerkat.bulletinboard.sqlserver.H2QueryProvider;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.Result;
|
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ThreadMXBean;
|
||||||
import java.lang.management.ThreadMXBean;
|
import java.sql.*;
|
||||||
import java.sql.*;
|
import java.util.List;
|
||||||
import java.util.List;
|
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
/**
|
||||||
/**
|
* Created by Arbel Deutsch Peled on 07-Dec-15.
|
||||||
* Created by Arbel Deutsch Peled on 07-Dec-15.
|
*/
|
||||||
*/
|
public class H2BulletinBoardServerTest {
|
||||||
public class H2BulletinBoardServerTest {
|
|
||||||
|
private final String dbName = "meerkatTest";
|
||||||
private final String dbName = "meerkatTest";
|
|
||||||
|
private GenericBulletinBoardServerTest serverTest;
|
||||||
private GenericBulletinBoardServerTest serverTest;
|
|
||||||
|
private SQLQueryProvider queryProvider;
|
||||||
private SQLQueryProvider queryProvider;
|
|
||||||
|
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
|
||||||
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
|
|
||||||
|
@Before
|
||||||
@Before
|
public void init(){
|
||||||
public void init(){
|
|
||||||
|
System.err.println("Starting to initialize H2BulletinBoardServerTest");
|
||||||
System.err.println("Starting to initialize H2BulletinBoardServerTest");
|
long start = threadBean.getCurrentThreadCpuTime();
|
||||||
long start = threadBean.getCurrentThreadCpuTime();
|
|
||||||
|
queryProvider = new H2QueryProvider(dbName);
|
||||||
queryProvider = new H2QueryProvider(dbName);
|
|
||||||
|
try {
|
||||||
try {
|
|
||||||
|
Connection conn = queryProvider.getDataSource().getConnection();
|
||||||
Connection conn = queryProvider.getDataSource().getConnection();
|
Statement stmt = conn.createStatement();
|
||||||
Statement stmt = conn.createStatement();
|
|
||||||
|
List<String> deletionQueries = queryProvider.getSchemaDeletionCommands();
|
||||||
List<String> deletionQueries = queryProvider.getSchemaDeletionCommands();
|
|
||||||
|
for (String deletionQuery : deletionQueries) {
|
||||||
for (String deletionQuery : deletionQueries) {
|
stmt.execute(deletionQuery);
|
||||||
stmt.execute(deletionQuery);
|
}
|
||||||
}
|
|
||||||
|
} catch (SQLException e) {
|
||||||
} catch (SQLException e) {
|
System.err.println(e.getMessage());
|
||||||
System.err.println(e.getMessage());
|
fail(e.getMessage());
|
||||||
fail(e.getMessage());
|
}
|
||||||
}
|
|
||||||
|
BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(queryProvider);
|
||||||
BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(queryProvider);
|
try {
|
||||||
try {
|
bulletinBoardServer.init();
|
||||||
bulletinBoardServer.init("");
|
|
||||||
|
} catch (CommunicationException e) {
|
||||||
} catch (CommunicationException e) {
|
System.err.println(e.getMessage());
|
||||||
System.err.println(e.getMessage());
|
fail(e.getMessage());
|
||||||
fail(e.getMessage());
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
serverTest = new GenericBulletinBoardServerTest();
|
||||||
serverTest = new GenericBulletinBoardServerTest();
|
try {
|
||||||
try {
|
serverTest.init(bulletinBoardServer);
|
||||||
serverTest.init(bulletinBoardServer);
|
} catch (Exception e) {
|
||||||
} catch (Exception e) {
|
System.err.println(e.getMessage());
|
||||||
System.err.println(e.getMessage());
|
fail(e.getMessage());
|
||||||
fail(e.getMessage());
|
}
|
||||||
}
|
|
||||||
|
long end = threadBean.getCurrentThreadCpuTime();
|
||||||
long end = threadBean.getCurrentThreadCpuTime();
|
System.err.println("Finished initializing H2BulletinBoardServerTest");
|
||||||
System.err.println("Finished initializing H2BulletinBoardServerTest");
|
System.err.println("Time of operation: " + (end - start));
|
||||||
System.err.println("Time of operation: " + (end - start));
|
}
|
||||||
}
|
|
||||||
|
@Test
|
||||||
@Test
|
public void bulkTest() {
|
||||||
public void bulkTest() {
|
System.err.println("Starting bulkTest of H2BulletinBoardServerTest");
|
||||||
System.err.println("Starting bulkTest of H2BulletinBoardServerTest");
|
long start = threadBean.getCurrentThreadCpuTime();
|
||||||
long start = threadBean.getCurrentThreadCpuTime();
|
|
||||||
|
try {
|
||||||
try {
|
serverTest.testInsert();
|
||||||
serverTest.testInsert();
|
} catch (Exception e) {
|
||||||
} catch (Exception e) {
|
System.err.println(e.getMessage());
|
||||||
System.err.println(e.getMessage());
|
fail(e.getMessage());
|
||||||
fail(e.getMessage());
|
}
|
||||||
}
|
|
||||||
|
try{
|
||||||
try{
|
serverTest.testSimpleTagAndSignature();
|
||||||
serverTest.testSimpleTagAndSignature();
|
} catch (Exception e) {
|
||||||
} catch (Exception e) {
|
System.err.println(e.getMessage());
|
||||||
System.err.println(e.getMessage());
|
fail(e.getMessage());
|
||||||
fail(e.getMessage());
|
}
|
||||||
}
|
|
||||||
|
try{
|
||||||
try{
|
serverTest.testEnhancedTagsAndSignatures();
|
||||||
serverTest.testEnhancedTagsAndSignatures();
|
} catch (Exception e) {
|
||||||
} catch (Exception e) {
|
System.err.println(e.getMessage());
|
||||||
System.err.println(e.getMessage());
|
fail(e.getMessage());
|
||||||
fail(e.getMessage());
|
}
|
||||||
}
|
|
||||||
|
long end = threadBean.getCurrentThreadCpuTime();
|
||||||
long end = threadBean.getCurrentThreadCpuTime();
|
System.err.println("Finished bulkTest of H2BulletinBoardServerTest");
|
||||||
System.err.println("Finished bulkTest of H2BulletinBoardServerTest");
|
System.err.println("Time of operation: " + (end - start));
|
||||||
System.err.println("Time of operation: " + (end - start));
|
}
|
||||||
}
|
|
||||||
|
@Test
|
||||||
@Test
|
public void testBatch() {
|
||||||
public void testBatchPostAfterClose() {
|
|
||||||
try{
|
final int BATCH_NUM = 20;
|
||||||
serverTest.testBatchPostAfterClose();
|
|
||||||
} catch (Exception e) {
|
try{
|
||||||
System.err.println(e.getMessage());
|
for (int i = 0 ; i < BATCH_NUM ; i++) {
|
||||||
fail(e.getMessage());
|
serverTest.testPostBatch();
|
||||||
}
|
}
|
||||||
}
|
} catch (Exception e) {
|
||||||
|
System.err.println(e.getMessage());
|
||||||
@Test
|
fail(e.getMessage());
|
||||||
public void testBatch() {
|
}
|
||||||
|
|
||||||
final int BATCH_NUM = 20;
|
try{
|
||||||
|
serverTest.testReadBatch();
|
||||||
try{
|
} catch (Exception e) {
|
||||||
for (int i = 0 ; i < BATCH_NUM ; i++) {
|
System.err.println(e.getMessage());
|
||||||
serverTest.testPostBatch();
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println(e.getMessage());
|
}
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
@Test
|
||||||
|
public void testSyncQuery() {
|
||||||
try{
|
try {
|
||||||
serverTest.testReadBatch();
|
serverTest.testSyncQuery();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@After
|
||||||
@Test
|
public void close() {
|
||||||
public void testSyncQuery() {
|
System.err.println("Starting to close H2BulletinBoardServerTest");
|
||||||
try {
|
long start = threadBean.getCurrentThreadCpuTime();
|
||||||
serverTest.testSyncQuery();
|
|
||||||
} catch (Exception e) {
|
serverTest.close();
|
||||||
System.err.println(e.getMessage());
|
|
||||||
fail(e.getMessage());
|
long end = threadBean.getCurrentThreadCpuTime();
|
||||||
}
|
System.err.println("Finished closing H2BulletinBoardServerTest");
|
||||||
}
|
System.err.println("Time of operation: " + (end - start));
|
||||||
|
}
|
||||||
@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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,42 +1,42 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.rest.Constants;
|
import meerkat.rest.Constants;
|
||||||
import meerkat.rest.ProtobufMessageBodyReader;
|
import meerkat.rest.ProtobufMessageBodyReader;
|
||||||
import meerkat.rest.ProtobufMessageBodyWriter;
|
import meerkat.rest.ProtobufMessageBodyWriter;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import javax.ws.rs.client.Client;
|
import javax.ws.rs.client.Client;
|
||||||
import javax.ws.rs.client.ClientBuilder;
|
import javax.ws.rs.client.ClientBuilder;
|
||||||
import javax.ws.rs.client.WebTarget;
|
import javax.ws.rs.client.WebTarget;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by talm on 10/11/15.
|
* Created by talm on 10/11/15.
|
||||||
*/
|
*/
|
||||||
public class HelloProtoIntegrationTest {
|
public class HelloProtoIntegrationTest {
|
||||||
private static String PROP_GETTY_URL = "gretty.httpBaseURI";
|
private static String PROP_GETTY_URL = "gretty.httpBaseURI";
|
||||||
private static String DEFAULT_BASE_URL = "http://localhost:8081/";
|
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 BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL);
|
||||||
private static String HELLO_URL = "proto";
|
private static String HELLO_URL = "proto";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHello() throws Exception {
|
public void testHello() throws Exception {
|
||||||
Client client = ClientBuilder.newClient();
|
Client client = ClientBuilder.newClient();
|
||||||
client.register(ProtobufMessageBodyReader.class);
|
client.register(ProtobufMessageBodyReader.class);
|
||||||
client.register(ProtobufMessageBodyWriter.class);
|
client.register(ProtobufMessageBodyWriter.class);
|
||||||
|
|
||||||
WebTarget webTarget = client.target(BASE_URL).path(HELLO_URL);
|
WebTarget webTarget = client.target(BASE_URL).path(HELLO_URL);
|
||||||
BulletinBoardMessage response = webTarget.request(Constants.MEDIATYPE_PROTOBUF)
|
BulletinBoardMessage response = webTarget.request(Constants.MEDIATYPE_PROTOBUF)
|
||||||
.get(BulletinBoardMessage.class);
|
.get(BulletinBoardMessage.class);
|
||||||
|
|
||||||
System.out.println(response.getMsg().getData());
|
System.out.println(response.getMsg().getData());
|
||||||
|
|
||||||
assertThat(response.getMsg().getData().toStringUtf8(), is("Hello World!"));
|
assertThat(response.getMsg().getData().toStringUtf8(), is("Hello World!"));
|
||||||
assertThat(response.getMsg().getTagCount(), is(2));
|
assertThat(response.getMsg().getTagCount(), is(2));
|
||||||
assertThat(response.getMsg().getTag(0), is("Greetings"));
|
assertThat(response.getMsg().getTag(0), is("Greetings"));
|
||||||
assertThat(response.getMsg().getTag(1), is("FirstPrograms"));
|
assertThat(response.getMsg().getTag(1), is("FirstPrograms"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,172 +1,158 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
||||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider;
|
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider;
|
||||||
import meerkat.bulletinboard.sqlserver.MySQLQueryProvider;
|
import meerkat.bulletinboard.sqlserver.MySQLQueryProvider;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ThreadMXBean;
|
||||||
import java.lang.management.ThreadMXBean;
|
import java.sql.Connection;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.sql.SQLException;
|
||||||
import java.security.SignatureException;
|
import java.sql.Statement;
|
||||||
import java.sql.Connection;
|
import java.util.List;
|
||||||
import java.sql.DriverManager;
|
|
||||||
import java.sql.SQLException;
|
import static org.junit.Assert.fail;
|
||||||
import java.sql.Statement;
|
|
||||||
import java.util.List;
|
/**
|
||||||
|
* Created by Arbel Deutsch Peled on 07-Dec-15.
|
||||||
import static org.junit.Assert.fail;
|
*/
|
||||||
|
public class MySQLBulletinBoardServerTest {
|
||||||
/**
|
|
||||||
* Created by Arbel Deutsch Peled on 07-Dec-15.
|
private final String dbAddress = "localhost";
|
||||||
*/
|
private final int dbPort = 3306;
|
||||||
public class MySQLBulletinBoardServerTest {
|
private final String dbName = "meerkat";
|
||||||
|
private final String username = "arbel";
|
||||||
private final String dbAddress = "localhost";
|
private final String password = "mypass";
|
||||||
private final int dbPort = 3306;
|
|
||||||
private final String dbName = "meerkat";
|
private GenericBulletinBoardServerTest serverTest;
|
||||||
private final String username = "arbel";
|
|
||||||
private final String password = "mypass";
|
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
|
||||||
|
|
||||||
private GenericBulletinBoardServerTest serverTest;
|
@Before
|
||||||
|
public void init(){
|
||||||
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
|
|
||||||
|
System.err.println("Starting to initialize MySQLBulletinBoardServerTest");
|
||||||
@Before
|
long start = threadBean.getCurrentThreadCpuTime();
|
||||||
public void init(){
|
|
||||||
|
SQLQueryProvider queryProvider = new MySQLQueryProvider(dbAddress,dbPort,dbName,username,password);
|
||||||
System.err.println("Starting to initialize MySQLBulletinBoardServerTest");
|
|
||||||
long start = threadBean.getCurrentThreadCpuTime();
|
try {
|
||||||
|
|
||||||
SQLQueryProvider queryProvider = new MySQLQueryProvider(dbAddress,dbPort,dbName,username,password);
|
Connection conn = queryProvider.getDataSource().getConnection();
|
||||||
|
Statement stmt = conn.createStatement();
|
||||||
try {
|
|
||||||
|
List<String> deletionQueries = queryProvider.getSchemaDeletionCommands();
|
||||||
Connection conn = queryProvider.getDataSource().getConnection();
|
|
||||||
Statement stmt = conn.createStatement();
|
for (String deletionQuery : deletionQueries) {
|
||||||
|
stmt.execute(deletionQuery);
|
||||||
List<String> deletionQueries = queryProvider.getSchemaDeletionCommands();
|
}
|
||||||
|
|
||||||
for (String deletionQuery : deletionQueries) {
|
} catch (SQLException e) {
|
||||||
stmt.execute(deletionQuery);
|
System.err.println(e.getMessage());
|
||||||
}
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
} catch (SQLException e) {
|
|
||||||
System.err.println(e.getMessage());
|
BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(queryProvider);
|
||||||
fail(e.getMessage());
|
try {
|
||||||
}
|
bulletinBoardServer.init();
|
||||||
|
|
||||||
BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(queryProvider);
|
} catch (CommunicationException e) {
|
||||||
try {
|
System.err.println(e.getMessage());
|
||||||
bulletinBoardServer.init("");
|
fail(e.getMessage());
|
||||||
|
return;
|
||||||
} catch (CommunicationException e) {
|
}
|
||||||
System.err.println(e.getMessage());
|
|
||||||
fail(e.getMessage());
|
serverTest = new GenericBulletinBoardServerTest();
|
||||||
return;
|
try {
|
||||||
}
|
serverTest.init(bulletinBoardServer);
|
||||||
|
} catch (Exception e) {
|
||||||
serverTest = new GenericBulletinBoardServerTest();
|
System.err.println(e.getMessage());
|
||||||
try {
|
fail(e.getMessage());
|
||||||
serverTest.init(bulletinBoardServer);
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println(e.getMessage());
|
long end = threadBean.getCurrentThreadCpuTime();
|
||||||
fail(e.getMessage());
|
System.err.println("Finished initializing MySQLBulletinBoardServerTest");
|
||||||
}
|
System.err.println("Time of operation: " + (end - start));
|
||||||
|
}
|
||||||
long end = threadBean.getCurrentThreadCpuTime();
|
|
||||||
System.err.println("Finished initializing MySQLBulletinBoardServerTest");
|
@Test
|
||||||
System.err.println("Time of operation: " + (end - start));
|
public void bulkTest() {
|
||||||
}
|
System.err.println("Starting bulkTest of MySQLBulletinBoardServerTest");
|
||||||
|
long start = threadBean.getCurrentThreadCpuTime();
|
||||||
@Test
|
|
||||||
public void bulkTest() {
|
try {
|
||||||
System.err.println("Starting bulkTest of MySQLBulletinBoardServerTest");
|
serverTest.testInsert();
|
||||||
long start = threadBean.getCurrentThreadCpuTime();
|
} catch (Exception e) {
|
||||||
|
System.err.println(e.getMessage());
|
||||||
try {
|
fail(e.getMessage());
|
||||||
serverTest.testInsert();
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println(e.getMessage());
|
try{
|
||||||
fail(e.getMessage());
|
serverTest.testSimpleTagAndSignature();
|
||||||
}
|
} catch (Exception e) {
|
||||||
|
System.err.println(e.getMessage());
|
||||||
try{
|
fail(e.getMessage());
|
||||||
serverTest.testSimpleTagAndSignature();
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println(e.getMessage());
|
try{
|
||||||
fail(e.getMessage());
|
serverTest.testEnhancedTagsAndSignatures();
|
||||||
}
|
} catch (Exception e) {
|
||||||
|
System.err.println(e.getMessage());
|
||||||
try{
|
fail(e.getMessage());
|
||||||
serverTest.testEnhancedTagsAndSignatures();
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println(e.getMessage());
|
long end = threadBean.getCurrentThreadCpuTime();
|
||||||
fail(e.getMessage());
|
System.err.println("Finished bulkTest of MySQLBulletinBoardServerTest");
|
||||||
}
|
System.err.println("Time of operation: " + (end - start));
|
||||||
|
}
|
||||||
long end = threadBean.getCurrentThreadCpuTime();
|
|
||||||
System.err.println("Finished bulkTest of MySQLBulletinBoardServerTest");
|
@Test
|
||||||
System.err.println("Time of operation: " + (end - start));
|
public void testBatch() {
|
||||||
}
|
|
||||||
|
final int BATCH_NUM = 20;
|
||||||
@Test
|
|
||||||
public void testBatchPostAfterClose() {
|
try{
|
||||||
try{
|
for (int i = 0 ; i < BATCH_NUM ; i++) {
|
||||||
serverTest.testBatchPostAfterClose();
|
serverTest.testPostBatch();
|
||||||
} catch (Exception e) {
|
}
|
||||||
System.err.println(e.getMessage());
|
} catch (Exception e) {
|
||||||
fail(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
}
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
try{
|
||||||
public void testBatch() {
|
serverTest.testReadBatch();
|
||||||
|
} catch (Exception e) {
|
||||||
final int BATCH_NUM = 20;
|
System.err.println(e.getMessage());
|
||||||
|
fail(e.getMessage());
|
||||||
try{
|
}
|
||||||
for (int i = 0 ; i < BATCH_NUM ; i++) {
|
|
||||||
serverTest.testPostBatch();
|
}
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
@Test
|
||||||
System.err.println(e.getMessage());
|
public void testSyncQuery() {
|
||||||
fail(e.getMessage());
|
try {
|
||||||
}
|
serverTest.testSyncQuery();
|
||||||
|
} catch (Exception e) {
|
||||||
try{
|
System.err.println(e.getMessage());
|
||||||
serverTest.testReadBatch();
|
fail(e.getMessage());
|
||||||
} 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();
|
||||||
@Test
|
|
||||||
public void testSyncQuery() {
|
serverTest.close();
|
||||||
try {
|
|
||||||
serverTest.testSyncQuery();
|
long end = threadBean.getCurrentThreadCpuTime();
|
||||||
} catch (Exception e) {
|
System.err.println("Finished closing MySQLBulletinBoardServerTest");
|
||||||
System.err.println(e.getMessage());
|
System.err.println("Time of operation: " + (end - start));
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,106 +1,129 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
||||||
import meerkat.bulletinboard.sqlserver.SQLiteQueryProvider;
|
import meerkat.bulletinboard.sqlserver.SQLiteQueryProvider;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.protobuf.*;
|
import meerkat.protobuf.*;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.lang.management.ThreadMXBean;
|
import java.lang.management.ThreadMXBean;
|
||||||
import java.security.*;
|
import java.security.*;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
|
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 07-Dec-15.
|
* Created by Arbel Deutsch Peled on 07-Dec-15.
|
||||||
*/
|
*/
|
||||||
public class SQLiteBulletinBoardServerTest{
|
public class SQLiteBulletinBoardServerTest{
|
||||||
|
|
||||||
private String testFilename = "SQLiteDBTest.db";
|
private String testFilename = "SQLiteDBTest.db";
|
||||||
|
|
||||||
private GenericBulletinBoardServerTest serverTest;
|
private GenericBulletinBoardServerTest serverTest;
|
||||||
|
|
||||||
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
|
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init(){
|
public void init(){
|
||||||
|
|
||||||
System.err.println("Starting to initialize SQLiteBulletinBoardServerTest");
|
System.err.println("Starting to initialize SQLiteBulletinBoardServerTest");
|
||||||
long start = threadBean.getCurrentThreadCpuTime();
|
long start = threadBean.getCurrentThreadCpuTime();
|
||||||
|
|
||||||
File old = new File(testFilename);
|
File old = new File(testFilename);
|
||||||
old.delete();
|
old.delete();
|
||||||
|
|
||||||
BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(new SQLiteQueryProvider(testFilename));
|
BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(new SQLiteQueryProvider(testFilename));
|
||||||
try {
|
try {
|
||||||
bulletinBoardServer.init("");
|
bulletinBoardServer.init();
|
||||||
|
|
||||||
} catch (CommunicationException e) {
|
} catch (CommunicationException e) {
|
||||||
System.err.println(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
serverTest = new GenericBulletinBoardServerTest();
|
serverTest = new GenericBulletinBoardServerTest();
|
||||||
try {
|
try {
|
||||||
serverTest.init(bulletinBoardServer);
|
serverTest.init(bulletinBoardServer);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
long end = threadBean.getCurrentThreadCpuTime();
|
long end = threadBean.getCurrentThreadCpuTime();
|
||||||
System.err.println("Finished initializing SQLiteBulletinBoardServerTest");
|
System.err.println("Finished initializing SQLiteBulletinBoardServerTest");
|
||||||
System.err.println("Time of operation: " + (end - start));
|
System.err.println("Time of operation: " + (end - start));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
// @Test
|
||||||
public void bulkTest() {
|
public void bulkTest() {
|
||||||
System.err.println("Starting bulkTest of SQLiteBulletinBoardServerTest");
|
System.err.println("Starting bulkTest of SQLiteBulletinBoardServerTest");
|
||||||
long start = threadBean.getCurrentThreadCpuTime();
|
long start = threadBean.getCurrentThreadCpuTime();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
serverTest.testInsert();
|
serverTest.testInsert();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
serverTest.testSimpleTagAndSignature();
|
serverTest.testSimpleTagAndSignature();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
serverTest.testEnhancedTagsAndSignatures();
|
serverTest.testEnhancedTagsAndSignatures();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
long end = threadBean.getCurrentThreadCpuTime();
|
long end = threadBean.getCurrentThreadCpuTime();
|
||||||
System.err.println("Finished bulkTest of SQLiteBulletinBoardServerTest");
|
System.err.println("Finished bulkTest of SQLiteBulletinBoardServerTest");
|
||||||
System.err.println("Time of operation: " + (end - start));
|
System.err.println("Time of operation: " + (end - start));
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
// @Test
|
||||||
public void close() {
|
public void testBatch() {
|
||||||
System.err.println("Starting to close SQLiteBulletinBoardServerTest");
|
|
||||||
long start = threadBean.getCurrentThreadCpuTime();
|
final int BATCH_NUM = 20;
|
||||||
|
|
||||||
serverTest.close();
|
try{
|
||||||
|
for (int i = 0 ; i < BATCH_NUM ; i++) {
|
||||||
long end = threadBean.getCurrentThreadCpuTime();
|
serverTest.testPostBatch();
|
||||||
System.err.println("Finished closing SQLiteBulletinBoardServerTest");
|
}
|
||||||
System.err.println("Time of operation: " + (end - start));
|
} 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,223 +1,223 @@
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "us.kirchmeier.capsule" version "1.0.1"
|
id "us.kirchmeier.capsule" version "1.0.1"
|
||||||
id 'com.google.protobuf' version '0.7.0'
|
id 'com.google.protobuf' version '0.7.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'eclipse'
|
apply plugin: 'eclipse'
|
||||||
apply plugin: 'idea'
|
apply plugin: 'idea'
|
||||||
|
|
||||||
apply plugin: 'maven-publish'
|
apply plugin: 'maven-publish'
|
||||||
|
|
||||||
// Uncomment the lines below to define an application
|
// Uncomment the lines below to define an application
|
||||||
// (this will also allow you to build a "fatCapsule" which includes
|
// (this will also allow you to build a "fatCapsule" which includes
|
||||||
// the entire application, including all dependencies in a single jar)
|
// the entire application, including all dependencies in a single jar)
|
||||||
//apply plugin: 'application'
|
//apply plugin: 'application'
|
||||||
//mainClassName='your.main.ApplicationClass'
|
//mainClassName='your.main.ApplicationClass'
|
||||||
|
|
||||||
// Is this a snapshot version?
|
// Is this a snapshot version?
|
||||||
ext { isSnapshot = false }
|
ext { isSnapshot = false }
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
groupId = 'org.factcenter.meerkat'
|
groupId = 'org.factcenter.meerkat'
|
||||||
nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/"
|
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)
|
// Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing)
|
||||||
// Should be set in ${HOME}/.gradle/gradle.properties
|
// Should be set in ${HOME}/.gradle/gradle.properties
|
||||||
nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : ""
|
nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : ""
|
||||||
nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : ""
|
nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
description = "TODO: Add a description"
|
description = "TODO: Add a description"
|
||||||
|
|
||||||
// Your project version
|
// Your project version
|
||||||
version = "0.0"
|
version = "0.0"
|
||||||
|
|
||||||
version += "${isSnapshot ? '-SNAPSHOT' : ''}"
|
version += "${isSnapshot ? '-SNAPSHOT' : ''}"
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Meerkat common
|
// Meerkat common
|
||||||
compile project(':meerkat-common')
|
compile project(':meerkat-common')
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
compile 'org.slf4j:slf4j-api:1.7.7'
|
compile 'org.slf4j:slf4j-api:1.7.7'
|
||||||
runtime 'ch.qos.logback:logback-classic:1.1.2'
|
runtime 'ch.qos.logback:logback-classic:1.1.2'
|
||||||
runtime 'ch.qos.logback:logback-core:1.1.2'
|
runtime 'ch.qos.logback:logback-core:1.1.2'
|
||||||
|
|
||||||
// Google protobufs
|
// Google protobufs
|
||||||
compile 'com.google.protobuf:protobuf-java:3.+'
|
compile 'com.google.protobuf:protobuf-java:3.+'
|
||||||
|
|
||||||
// Depend on test resources from meerkat-common
|
// Depend on test resources from meerkat-common
|
||||||
testCompile project(path: ':meerkat-common', configuration: 'testOutput')
|
testCompile project(path: ':meerkat-common', configuration: 'testOutput')
|
||||||
|
|
||||||
testCompile 'junit:junit:4.+'
|
testCompile 'junit:junit:4.+'
|
||||||
|
|
||||||
runtime 'org.codehaus.groovy:groovy:2.4.+'
|
runtime 'org.codehaus.groovy:groovy:2.4.+'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*==== You probably don't have to edit below this line =======*/
|
/*==== You probably don't have to edit below this line =======*/
|
||||||
|
|
||||||
// Setup test configuration that can appear as a dependency in
|
// Setup test configuration that can appear as a dependency in
|
||||||
// other subprojects
|
// other subprojects
|
||||||
configurations {
|
configurations {
|
||||||
testOutput.extendsFrom (testCompile)
|
testOutput.extendsFrom (testCompile)
|
||||||
}
|
}
|
||||||
|
|
||||||
task testJar(type: Jar, dependsOn: testClasses) {
|
task testJar(type: Jar, dependsOn: testClasses) {
|
||||||
classifier = 'tests'
|
classifier = 'tests'
|
||||||
from sourceSets.test.output
|
from sourceSets.test.output
|
||||||
}
|
}
|
||||||
|
|
||||||
artifacts {
|
artifacts {
|
||||||
testOutput testJar
|
testOutput testJar
|
||||||
}
|
}
|
||||||
|
|
||||||
// The run task added by the application plugin
|
// The run task added by the application plugin
|
||||||
// is also of type JavaExec.
|
// is also of type JavaExec.
|
||||||
tasks.withType(JavaExec) {
|
tasks.withType(JavaExec) {
|
||||||
// Assign all Java system properties from
|
// Assign all Java system properties from
|
||||||
// the command line to the JavaExec task.
|
// the command line to the JavaExec task.
|
||||||
systemProperties System.properties
|
systemProperties System.properties
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protobuf {
|
protobuf {
|
||||||
// Configure the protoc executable
|
// Configure the protoc executable
|
||||||
protoc {
|
protoc {
|
||||||
// Download from repositories
|
// Download from repositories
|
||||||
artifact = 'com.google.protobuf:protoc:3.+'
|
artifact = 'com.google.protobuf:protoc:3.+'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
idea {
|
idea {
|
||||||
module {
|
module {
|
||||||
project.sourceSets.each { sourceSet ->
|
project.sourceSets.each { sourceSet ->
|
||||||
|
|
||||||
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
||||||
|
|
||||||
println "Adding $srcDir"
|
println "Adding $srcDir"
|
||||||
// add protobuf generated sources to generated source dir.
|
// add protobuf generated sources to generated source dir.
|
||||||
if ("test".equals(sourceSet.name)) {
|
if ("test".equals(sourceSet.name)) {
|
||||||
testSourceDirs += file(srcDir)
|
testSourceDirs += file(srcDir)
|
||||||
} else {
|
} else {
|
||||||
sourceDirs += file(srcDir)
|
sourceDirs += file(srcDir)
|
||||||
}
|
}
|
||||||
generatedSourceDirs += file(srcDir)
|
generatedSourceDirs += file(srcDir)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't exclude build directory
|
// Don't exclude build directory
|
||||||
excludeDirs -= file(buildDir)
|
excludeDirs -= file(buildDir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*===================================
|
/*===================================
|
||||||
* "Fat" Build targets
|
* "Fat" Build targets
|
||||||
*===================================*/
|
*===================================*/
|
||||||
|
|
||||||
|
|
||||||
if (project.hasProperty('mainClassName') && (mainClassName != null)) {
|
if (project.hasProperty('mainClassName') && (mainClassName != null)) {
|
||||||
|
|
||||||
task mavenCapsule(type: MavenCapsule) {
|
task mavenCapsule(type: MavenCapsule) {
|
||||||
description = "Generate a capsule jar that automatically downloads and caches dependencies when run."
|
description = "Generate a capsule jar that automatically downloads and caches dependencies when run."
|
||||||
applicationClass mainClassName
|
applicationClass mainClassName
|
||||||
destinationDir = buildDir
|
destinationDir = buildDir
|
||||||
}
|
}
|
||||||
|
|
||||||
task fatCapsule(type: FatCapsule) {
|
task fatCapsule(type: FatCapsule) {
|
||||||
description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class"
|
description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class"
|
||||||
|
|
||||||
destinationDir = buildDir
|
destinationDir = buildDir
|
||||||
|
|
||||||
def fatMain = hasProperty('fatmain') ? fatmain : mainClassName
|
def fatMain = hasProperty('fatmain') ? fatmain : mainClassName
|
||||||
|
|
||||||
applicationClass fatMain
|
applicationClass fatMain
|
||||||
|
|
||||||
def testJar = hasProperty('test')
|
def testJar = hasProperty('test')
|
||||||
|
|
||||||
if (hasProperty('fatmain')) {
|
if (hasProperty('fatmain')) {
|
||||||
appendix = "fat-${fatMain}"
|
appendix = "fat-${fatMain}"
|
||||||
} else {
|
} else {
|
||||||
appendix = "fat"
|
appendix = "fat"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (testJar) {
|
if (testJar) {
|
||||||
from sourceSets.test.output
|
from sourceSets.test.output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*===================================
|
/*===================================
|
||||||
* Repositories
|
* Repositories
|
||||||
*===================================*/
|
*===================================*/
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
|
||||||
// Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral)
|
// Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral)
|
||||||
maven {
|
maven {
|
||||||
url nexusRepository
|
url nexusRepository
|
||||||
|
|
||||||
if (isSnapshot) {
|
if (isSnapshot) {
|
||||||
credentials { username
|
credentials { username
|
||||||
password
|
password
|
||||||
|
|
||||||
username nexusUser
|
username nexusUser
|
||||||
password nexusPassword
|
password nexusPassword
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use local maven repository
|
// Use local maven repository
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
|
|
||||||
// Use 'maven central' for other dependencies.
|
// Use 'maven central' for other dependencies.
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
task "info" << {
|
task "info" << {
|
||||||
println "Project: ${project.name}"
|
println "Project: ${project.name}"
|
||||||
println "Description: ${project.description}"
|
println "Description: ${project.description}"
|
||||||
println "--------------------------"
|
println "--------------------------"
|
||||||
println "GroupId: $groupId"
|
println "GroupId: $groupId"
|
||||||
println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})"
|
println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})"
|
||||||
println ""
|
println ""
|
||||||
}
|
}
|
||||||
info.description 'Print some information about project parameters'
|
info.description 'Print some information about project parameters'
|
||||||
|
|
||||||
|
|
||||||
/*===================================
|
/*===================================
|
||||||
* Publishing
|
* Publishing
|
||||||
*===================================*/
|
*===================================*/
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
publications {
|
publications {
|
||||||
mavenJava(MavenPublication) {
|
mavenJava(MavenPublication) {
|
||||||
groupId project.groupId
|
groupId project.groupId
|
||||||
pom.withXml {
|
pom.withXml {
|
||||||
asNode().appendNode('description', project.description)
|
asNode().appendNode('description', project.description)
|
||||||
}
|
}
|
||||||
from project.components.java
|
from project.components.java
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}"
|
url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}"
|
||||||
credentials { username
|
credentials { username
|
||||||
password
|
password
|
||||||
|
|
||||||
username nexusUser
|
username nexusUser
|
||||||
password nexusPassword
|
password nexusPassword
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,46 +1,46 @@
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
package meerkat;
|
package meerkat;
|
||||||
|
|
||||||
option java_package = "meerkat.protobuf";
|
option java_package = "meerkat.protobuf";
|
||||||
|
|
||||||
message Payload {
|
message Payload {
|
||||||
enum Type {
|
enum Type {
|
||||||
SHARE = 0;
|
SHARE = 0;
|
||||||
COMMITMENT = 1;
|
COMMITMENT = 1;
|
||||||
COMPLAINT = 2;
|
COMPLAINT = 2;
|
||||||
DONE = 3;
|
DONE = 3;
|
||||||
ANSWER = 4;
|
ANSWER = 4;
|
||||||
YCOMMITMENT = 5;
|
YCOMMITMENT = 5;
|
||||||
YCOMPLAINT = 6;
|
YCOMPLAINT = 6;
|
||||||
YANSWER = 7;
|
YANSWER = 7;
|
||||||
ABORT = 8;
|
ABORT = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Type of message in protocol
|
// Type of message in protocol
|
||||||
Type type = 1;
|
Type type = 1;
|
||||||
|
|
||||||
oneof payload_data {
|
oneof payload_data {
|
||||||
IDMessage id = 5;
|
IDMessage id = 5;
|
||||||
ShareMessage share = 6;
|
ShareMessage share = 6;
|
||||||
CommitmentMessage commitment = 7;
|
CommitmentMessage commitment = 7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message IDMessage {
|
message IDMessage {
|
||||||
int32 id = 1;
|
int32 id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ShareMessage {
|
message ShareMessage {
|
||||||
int32 i = 1;
|
int32 i = 1;
|
||||||
int32 j = 2;
|
int32 j = 2;
|
||||||
bytes share = 3;
|
bytes share = 3;
|
||||||
// For double shares (used in GJKR protocol)
|
// For double shares (used in GJKR protocol)
|
||||||
bytes share_t = 4;
|
bytes share_t = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CommitmentMessage {
|
message CommitmentMessage {
|
||||||
int32 k = 1;
|
int32 k = 1;
|
||||||
bytes commitment = 2;
|
bytes commitment = 2;
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
|
@ -1,6 +1,7 @@
|
||||||
#Fri Jan 29 21:00:29 IST 2016
|
#Tue Aug 05 03:26:05 IDT 2014
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.9-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-2.9-all.zip
|
||||||
|
distributionSha256Sum=4647967f8de78d6d6d8093cdac50f368f8c2b8038f41a5afe1c3bce4c69219a9
|
||||||
|
|
|
@ -42,6 +42,11 @@ case "`uname`" in
|
||||||
;;
|
;;
|
||||||
esac
|
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
|
# Attempt to set APP_HOME
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
PRG="$0"
|
||||||
|
@ -56,9 +61,9 @@ while [ -h "$PRG" ] ; do
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
SAVED="`pwd`"
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
cd "`dirname \"$PRG\"`/" >&-
|
||||||
APP_HOME="`pwd -P`"
|
APP_HOME="`pwd -P`"
|
||||||
cd "$SAVED" >/dev/null
|
cd "$SAVED" >&-
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
@ -109,7 +114,6 @@ fi
|
||||||
if $cygwin ; then
|
if $cygwin ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
/bin/
|
/bin/
|
||||||
|
|
|
@ -1,219 +1,219 @@
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "us.kirchmeier.capsule" version "1.0.1"
|
id "us.kirchmeier.capsule" version "1.0.1"
|
||||||
id 'com.google.protobuf' version '0.7.0'
|
id 'com.google.protobuf' version '0.7.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'com.google.protobuf'
|
apply plugin: 'com.google.protobuf'
|
||||||
apply plugin: 'eclipse'
|
apply plugin: 'eclipse'
|
||||||
apply plugin: 'idea'
|
apply plugin: 'idea'
|
||||||
|
|
||||||
apply plugin: 'application'
|
apply plugin: 'application'
|
||||||
|
|
||||||
apply plugin: 'maven-publish'
|
apply plugin: 'maven-publish'
|
||||||
|
|
||||||
mainClassName='Demo'
|
mainClassName='Demo'
|
||||||
|
|
||||||
// Is this a snapshot version?
|
// Is this a snapshot version?
|
||||||
ext { isSnapshot = false }
|
ext { isSnapshot = false }
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
groupId = 'org.factcenter.meerkat'
|
groupId = 'org.factcenter.meerkat'
|
||||||
nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/"
|
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)
|
// Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing)
|
||||||
// Should be set in ${HOME}/.gradle/gradle.properties
|
// Should be set in ${HOME}/.gradle/gradle.properties
|
||||||
nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : ""
|
nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : ""
|
||||||
nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : ""
|
nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
description = "Meerkat Voting Common Library"
|
description = "Meerkat Voting Common Library"
|
||||||
|
|
||||||
// Your project version
|
// Your project version
|
||||||
version = "0.0"
|
version = "0.0"
|
||||||
|
|
||||||
version += "${isSnapshot ? '-SNAPSHOT' : ''}"
|
version += "${isSnapshot ? '-SNAPSHOT' : ''}"
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Logging
|
// Logging
|
||||||
compile 'org.slf4j:slf4j-api:1.7.7'
|
compile 'org.slf4j:slf4j-api:1.7.7'
|
||||||
compile 'javax.ws.rs:javax.ws.rs-api:2.0.+'
|
compile 'javax.ws.rs:javax.ws.rs-api:2.0.+'
|
||||||
runtime 'ch.qos.logback:logback-classic:1.1.2'
|
runtime 'ch.qos.logback:logback-classic:1.1.2'
|
||||||
runtime 'ch.qos.logback:logback-core:1.1.2'
|
runtime 'ch.qos.logback:logback-core:1.1.2'
|
||||||
|
|
||||||
// Google protobufs
|
// Google protobufs
|
||||||
compile 'com.google.protobuf:protobuf-java:3.+'
|
compile 'com.google.protobuf:protobuf-java:3.+'
|
||||||
|
|
||||||
// ListeningExecutor
|
// ListeningExecutor
|
||||||
compile 'com.google.guava:guava:15.0'
|
compile 'com.google.guava:guava:15.0'
|
||||||
|
|
||||||
// Crypto
|
// Crypto
|
||||||
compile 'org.factcenter.qilin:qilin:1.2+'
|
compile 'org.factcenter.qilin:qilin:1.2+'
|
||||||
compile 'org.bouncycastle:bcprov-jdk15on:1.53'
|
compile 'org.bouncycastle:bcprov-jdk15on:1.53'
|
||||||
|
|
||||||
testCompile 'junit:junit:4.+'
|
testCompile 'junit:junit:4.+'
|
||||||
|
|
||||||
runtime 'org.codehaus.groovy:groovy:2.4.+'
|
runtime 'org.codehaus.groovy:groovy:2.4.+'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*==== You probably don't have to edit below this line =======*/
|
/*==== You probably don't have to edit below this line =======*/
|
||||||
|
|
||||||
|
|
||||||
// Setup test configuration that can appear as a dependency in
|
// Setup test configuration that can appear as a dependency in
|
||||||
// other subprojects
|
// other subprojects
|
||||||
configurations {
|
configurations {
|
||||||
testOutput.extendsFrom (testCompile)
|
testOutput.extendsFrom (testCompile)
|
||||||
}
|
}
|
||||||
|
|
||||||
task testJar(type: Jar, dependsOn: testClasses) {
|
task testJar(type: Jar, dependsOn: testClasses) {
|
||||||
classifier = 'tests'
|
classifier = 'tests'
|
||||||
from sourceSets.test.output
|
from sourceSets.test.output
|
||||||
}
|
}
|
||||||
|
|
||||||
artifacts {
|
artifacts {
|
||||||
testOutput testJar
|
testOutput testJar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// The run task added by the application plugin
|
// The run task added by the application plugin
|
||||||
// is also of type JavaExec.
|
// is also of type JavaExec.
|
||||||
tasks.withType(JavaExec) {
|
tasks.withType(JavaExec) {
|
||||||
// Assign all Java system properties from
|
// Assign all Java system properties from
|
||||||
// the command line to the JavaExec task.
|
// the command line to the JavaExec task.
|
||||||
systemProperties System.properties
|
systemProperties System.properties
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protobuf {
|
protobuf {
|
||||||
// Configure the protoc executable
|
// Configure the protoc executable
|
||||||
protoc {
|
protoc {
|
||||||
// Download from repositories
|
// Download from repositories
|
||||||
artifact = 'com.google.protobuf:protoc:3.+'
|
artifact = 'com.google.protobuf:protoc:3.+'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
idea {
|
idea {
|
||||||
module {
|
module {
|
||||||
project.sourceSets.each { sourceSet ->
|
project.sourceSets.each { sourceSet ->
|
||||||
|
|
||||||
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
||||||
|
|
||||||
// add protobuf generated sources to generated source dir.
|
// add protobuf generated sources to generated source dir.
|
||||||
if ("test".equals(sourceSet.name)) {
|
if ("test".equals(sourceSet.name)) {
|
||||||
testSourceDirs += file(srcDir)
|
testSourceDirs += file(srcDir)
|
||||||
} else {
|
} else {
|
||||||
sourceDirs += file(srcDir)
|
sourceDirs += file(srcDir)
|
||||||
}
|
}
|
||||||
generatedSourceDirs += file(srcDir)
|
generatedSourceDirs += file(srcDir)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't exclude build directory
|
// Don't exclude build directory
|
||||||
excludeDirs -= file(buildDir)
|
excludeDirs -= file(buildDir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*===================================
|
/*===================================
|
||||||
* "Fat" Build targets
|
* "Fat" Build targets
|
||||||
*===================================*/
|
*===================================*/
|
||||||
|
|
||||||
|
|
||||||
task mavenCapsule(type: MavenCapsule){
|
task mavenCapsule(type: MavenCapsule){
|
||||||
description = "Generate a capsule jar that automatically downloads and caches dependencies when run."
|
description = "Generate a capsule jar that automatically downloads and caches dependencies when run."
|
||||||
applicationClass mainClassName
|
applicationClass mainClassName
|
||||||
destinationDir = buildDir
|
destinationDir = buildDir
|
||||||
}
|
}
|
||||||
|
|
||||||
task fatCapsule(type: FatCapsule){
|
task fatCapsule(type: FatCapsule){
|
||||||
description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class"
|
description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class"
|
||||||
|
|
||||||
destinationDir = buildDir
|
destinationDir = buildDir
|
||||||
|
|
||||||
def fatMain = hasProperty('fatmain') ? fatmain : mainClassName
|
def fatMain = hasProperty('fatmain') ? fatmain : mainClassName
|
||||||
|
|
||||||
applicationClass fatMain
|
applicationClass fatMain
|
||||||
|
|
||||||
def testJar = hasProperty('test')
|
def testJar = hasProperty('test')
|
||||||
|
|
||||||
if (hasProperty('fatmain')) {
|
if (hasProperty('fatmain')) {
|
||||||
appendix = "fat-${fatMain}"
|
appendix = "fat-${fatMain}"
|
||||||
} else {
|
} else {
|
||||||
appendix = "fat"
|
appendix = "fat"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (testJar) {
|
if (testJar) {
|
||||||
from sourceSets.test.output
|
from sourceSets.test.output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*===================================
|
/*===================================
|
||||||
* Repositories
|
* Repositories
|
||||||
*===================================*/
|
*===================================*/
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
|
||||||
mavenLocal();
|
mavenLocal();
|
||||||
|
|
||||||
// Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral)
|
// Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral)
|
||||||
maven {
|
maven {
|
||||||
url nexusRepository
|
url nexusRepository
|
||||||
|
|
||||||
if (isSnapshot) {
|
if (isSnapshot) {
|
||||||
credentials { username
|
credentials { username
|
||||||
password
|
password
|
||||||
|
|
||||||
username nexusUser
|
username nexusUser
|
||||||
password nexusPassword
|
password nexusPassword
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use 'maven central' for other dependencies.
|
// Use 'maven central' for other dependencies.
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
task "info" << {
|
task "info" << {
|
||||||
println "Project: ${project.name}"
|
println "Project: ${project.name}"
|
||||||
println "Description: ${project.description}"
|
println "Description: ${project.description}"
|
||||||
println "--------------------------"
|
println "--------------------------"
|
||||||
println "GroupId: $groupId"
|
println "GroupId: $groupId"
|
||||||
println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})"
|
println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})"
|
||||||
println ""
|
println ""
|
||||||
}
|
}
|
||||||
info.description 'Print some information about project parameters'
|
info.description 'Print some information about project parameters'
|
||||||
|
|
||||||
|
|
||||||
/*===================================
|
/*===================================
|
||||||
* Publishing
|
* Publishing
|
||||||
*===================================*/
|
*===================================*/
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
publications {
|
publications {
|
||||||
mavenJava(MavenPublication) {
|
mavenJava(MavenPublication) {
|
||||||
groupId project.groupId
|
groupId project.groupId
|
||||||
pom.withXml {
|
pom.withXml {
|
||||||
asNode().appendNode('description', project.description)
|
asNode().appendNode('description', project.description)
|
||||||
}
|
}
|
||||||
from project.components.java
|
from project.components.java
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}"
|
url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}"
|
||||||
credentials { username
|
credentials { username
|
||||||
password
|
password
|
||||||
|
|
||||||
username nexusUser
|
username nexusUser
|
||||||
password nexusPassword
|
password nexusPassword
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import static meerkat.protobuf.BulletinBoardAPI.*;
|
import static meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by talm on 10/26/15.
|
* Created by talm on 10/26/15.
|
||||||
*/
|
*/
|
||||||
public class Demo {
|
public class Demo {
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
System.out.println("Nothing to see yet");
|
System.out.println("Nothing to see yet");
|
||||||
|
|
||||||
BulletinBoardMessage msg;
|
BulletinBoardMessage msg;
|
||||||
|
|
||||||
UnsignedBulletinBoardMessage msgContents = UnsignedBulletinBoardMessage.newBuilder()
|
UnsignedBulletinBoardMessage msgContents = UnsignedBulletinBoardMessage.newBuilder()
|
||||||
.addTag("test")
|
.addTag("test")
|
||||||
.setData(ByteString.copyFromUtf8("some data"))
|
.setData(ByteString.copyFromUtf8("some data"))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
msg = BulletinBoardMessage.newBuilder()
|
msg = BulletinBoardMessage.newBuilder()
|
||||||
.setMsg(msgContents)
|
.setMsg(msgContents)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
msg.writeTo(System.err);
|
msg.writeTo(System.err);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.Timestamp;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
import meerkat.protobuf.Crypto.Signature;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -13,6 +14,7 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Post a message to the bulletin board in an asynchronous manner
|
* 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 msg is the message to be posted
|
||||||
* @param callback is a class containing methods to handle the result of the operation
|
* @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
|
* @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<Boolean> callback);
|
public MessageID postMessage(BulletinBoardMessage msg, FutureCallback<Boolean> callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform an end-to-end post of a signed batch message
|
* Perform an end-to-end post of a message in batch form
|
||||||
* @param completeBatch contains all the data of the batch including the meta-data and the signature
|
* @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
|
* @param callback is a class containing methods to handle the result of the operation
|
||||||
* @return a unique identifier for the batch message
|
* @return a unique identifier for the batch message
|
||||||
*/
|
*/
|
||||||
public MessageID postBatch(CompleteBatch completeBatch, FutureCallback<Boolean> callback);
|
public MessageID postAsBatch(BulletinBoardMessage completeBatch, int chunkSize, FutureCallback<Boolean> 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
|
* 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
|
* @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<Boolean> callback);
|
public void beginBatch(Iterable<String> tags, FutureCallback<BatchIdentifier> callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method posts batch data into an (assumed to be open) batch
|
* This method posts batch data into an (assumed to be open) batch
|
||||||
* It does not close the batch
|
* It does not close the batch
|
||||||
* @param signerId is the canonical form for the ID of the sender of this batch
|
* @param batchIdentifier is the temporary batch identifier
|
||||||
* @param batchId is a unique (per signer) ID for this batch
|
* @param batchChunkList is the (canonically ordered) list of data comprising the portion of the batch to be posted
|
||||||
* @param batchDataList 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
|
* @param startPosition is the location (in the batch) of the first entry in batchDataList
|
||||||
* (optionally used to continue interrupted post operations)
|
* (optionally used to continue interrupted post operations)
|
||||||
* The first position in the batch is position 0
|
* The first position in the batch is position 0
|
||||||
* @param callback is a callback function class for handling results of the operation
|
* @param callback is a callback function class for handling results of the operation
|
||||||
|
* @throws IllegalArgumentException if the batch identifier given was of an illegal format
|
||||||
*/
|
*/
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList,
|
public void postBatchData(BatchIdentifier batchIdentifier, List<BatchChunk> batchChunkList,
|
||||||
int startPosition, FutureCallback<Boolean> callback);
|
int startPosition, FutureCallback<Boolean> callback) throws IllegalArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overloading of the postBatchData method which starts at the first position in the batch
|
* Overloading of the postBatchData method which starts at the first position in the batch
|
||||||
*/
|
*/
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback);
|
public void postBatchData(BatchIdentifier batchIdentifier, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback)
|
||||||
|
throws IllegalArgumentException;
|
||||||
/**
|
|
||||||
* Overloading of the postBatchData method which uses ByteString
|
|
||||||
*/
|
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList,
|
|
||||||
int startPosition, FutureCallback<Boolean> 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<BatchData> batchDataList, FutureCallback<Boolean> callback);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to close a batch message
|
* 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
|
* @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<Boolean> callback);
|
public void closeBatch(BatchIdentifier batchIdentifier, Timestamp timestamp, Iterable<Signature> signatures, FutureCallback<Boolean> callback)
|
||||||
|
throws IllegalArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check how "safe" a given message is in an asynchronous manner
|
* 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
|
* Read all messages posted matching the given filter in an asynchronous manner
|
||||||
* Note that if messages haven't been "fully posted", this might return a different
|
* Note that if messages haven't been "fully posted", this might return a different
|
||||||
* set of messages in different calls. However, messages that are fully posted
|
* set of messages in different calls. However, messages that are fully posted
|
||||||
* are guaranteed to be included.
|
* are guaranteed to be included
|
||||||
* @param filterList return only messages that match the filters (null means no filtering).
|
* 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
|
* @param callback is a callback function class for handling results of the operation
|
||||||
*/
|
*/
|
||||||
public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback);
|
public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a given batch message from the bulletin board
|
* Read a given message from the bulletin board
|
||||||
* @param batchSpecificationMessage contains the data required to specify a single batch instance
|
* If the message is a batch: returns a complete message containing the batch data as well as the metadata
|
||||||
|
* @param msgID is the ID of the message to be read
|
||||||
* @param callback is a callback class for handling the result of the operation
|
* @param callback is a callback class for handling the result of the operation
|
||||||
*/
|
*/
|
||||||
public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback<CompleteBatch> callback);
|
public void readMessage(MessageID msgID, FutureCallback<BulletinBoardMessage> 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<BulletinBoardMessage> callback) throws IllegalArgumentException;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,63 +1,92 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.protobuf.Voting.*;
|
import meerkat.protobuf.Voting.*;
|
||||||
|
|
||||||
import static meerkat.protobuf.BulletinBoardAPI.*;
|
import static meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.List;
|
||||||
import java.util.List;
|
|
||||||
|
/**
|
||||||
/**
|
* Created by talm on 24/10/15.
|
||||||
* Created by talm on 24/10/15.
|
*/
|
||||||
*/
|
public interface BulletinBoardClient {
|
||||||
public interface BulletinBoardClient {
|
|
||||||
|
/**
|
||||||
/**
|
* Initialize the client to use some specified servers
|
||||||
* Initialize the client to use some specified servers
|
* @param clientParams contains the parameters required for the client setup
|
||||||
* @param clientParams contains the parameters required for the client setup
|
*/
|
||||||
*/
|
void init(BulletinBoardClientParams clientParams);
|
||||||
void init(BulletinBoardClientParams clientParams);
|
|
||||||
|
/**
|
||||||
/**
|
* Post a message to the bulletin board in a synchronous manner
|
||||||
* 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
|
* @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
|
* @return a unique message ID for the message, that can be later used to retrieve the batch
|
||||||
* @throws CommunicationException
|
* @throws CommunicationException
|
||||||
*/
|
*/
|
||||||
MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException;
|
MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check how "safe" a given message is in a synchronous manner
|
* Check how "safe" a given message is in a synchronous manner
|
||||||
* @param id is the unique message identifier for retrieval
|
* @param id is the unique message identifier for retrieval
|
||||||
* @return a normalized "redundancy score" from 0 (local only) to 1 (fully published)
|
* @return a normalized "redundancy score" from 0 (local only) to 1 (fully published)
|
||||||
*/
|
* @throws CommunicationException
|
||||||
float getRedundancy(MessageID id);
|
*/
|
||||||
|
float getRedundancy(MessageID id) throws CommunicationException;
|
||||||
/**
|
|
||||||
* Read all messages posted matching the given filter in a synchronous manner
|
/**
|
||||||
* Note that if messages haven't been "fully posted", this might return a different
|
* Read all messages posted matching the given filter in a synchronous manner
|
||||||
* set of messages in different calls. However, messages that are fully posted
|
* Note that if messages haven't been "fully posted", this might return a different
|
||||||
* are guaranteed to be included.
|
* set of messages in different calls. However, messages that are fully posted
|
||||||
* @param filterList return only messages that match the filters (null means no filtering)
|
* are guaranteed to be included.
|
||||||
* @return the list of messages
|
* Also: batch messages are returned as stubs.
|
||||||
*/
|
* @param filterList return only messages that match the filters (null means no filtering)
|
||||||
List<BulletinBoardMessage> readMessages(MessageFilterList filterList);
|
* @return the list of messages
|
||||||
|
*/
|
||||||
/**
|
List<BulletinBoardMessage> readMessages(MessageFilterList filterList) throws CommunicationException;
|
||||||
* 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
|
* Breaks up a bulletin board message into chunks and posts it as a batch message
|
||||||
* These are represented as fractions of the total number of relevant messages
|
* @param msg is the message to post
|
||||||
* @return The generated SyncQuery
|
* @param chunkSize is the maximal chunk size in bytes
|
||||||
* @throws CommunicationException when no DB can be contacted
|
* @return the unique message ID
|
||||||
*/
|
* @throws CommunicationException if operation is unsuccessful
|
||||||
SyncQuery generateSyncQuery(GenerateSyncQueryParams GenerateSyncQueryParams) throws CommunicationException;
|
*/
|
||||||
|
MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException;
|
||||||
/**
|
|
||||||
* Closes all connections, if any.
|
/**
|
||||||
* This is done in a synchronous (blocking) way.
|
* 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
|
||||||
void close();
|
* @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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ public interface BulletinBoardConstants {
|
||||||
public static final String BULLETIN_BOARD_SERVER_PATH = "/bbserver";
|
public static final String BULLETIN_BOARD_SERVER_PATH = "/bbserver";
|
||||||
public static final String GENERATE_SYNC_QUERY_PATH = "/generatesyncquery";
|
public static final String GENERATE_SYNC_QUERY_PATH = "/generatesyncquery";
|
||||||
public static final String READ_MESSAGES_PATH = "/readmessages";
|
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 READ_BATCH_PATH = "/readbatch";
|
||||||
public static final String POST_MESSAGE_PATH = "/postmessage";
|
public static final String POST_MESSAGE_PATH = "/postmessage";
|
||||||
public static final String BEGIN_BATCH_PATH = "/beginbatch";
|
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 CLOSE_BATCH_PATH = "/closebatch";
|
||||||
public static final String SYNC_QUERY_PATH = "/syncquery";
|
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#";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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<Boolean> 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<Boolean> 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;
|
||||||
|
|
||||||
|
}
|
|
@ -1,104 +1,110 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import com.google.protobuf.BoolValue;
|
import com.google.protobuf.BoolValue;
|
||||||
import meerkat.comm.CommunicationException;
|
import com.google.protobuf.Int32Value;
|
||||||
import meerkat.comm.MessageOutputStream;
|
import com.google.protobuf.Int64Value;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.comm.CommunicationException;
|
||||||
|
import meerkat.comm.MessageOutputStream;
|
||||||
import java.util.Collection;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel on 07/11/15.
|
* Created by Arbel on 07/11/15.
|
||||||
*
|
*
|
||||||
* This interface refers to a single instance of a Bulletin Board
|
* 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.
|
* An implementation of this interface may use any DB and be hosted on any machine.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public interface BulletinBoardServer{
|
public interface BulletinBoardServer{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method initializes the server by reading the signature data and storing it
|
* This method initializes the server by reading the signature data and storing it
|
||||||
* It also establishes the connection to the DB
|
* It also establishes the connection to the DB
|
||||||
* @throws CommunicationException on DB connection error
|
* @throws CommunicationException on DB connection error
|
||||||
*/
|
*/
|
||||||
public void init(String meerkatDB) throws CommunicationException;
|
public void init() throws CommunicationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Post a message to bulletin board.
|
* Post a message to bulletin board.
|
||||||
* @param msg is the actual (signed) message
|
* @param msg is the actual (signed) message
|
||||||
* @return TRUE if the message has been authenticated and FALSE otherwise (in ProtoBuf form)
|
* @return TRUE if the message has been authenticated and FALSE otherwise (in ProtoBuf form)
|
||||||
* @throws CommunicationException on DB connection error
|
* @throws CommunicationException on DB connection error
|
||||||
*/
|
*/
|
||||||
public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException;
|
public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read all messages posted matching the given filter
|
* Read all posted messages matching the given filters
|
||||||
* @param filterList return only messages that match the filters (empty list or null means no filtering)
|
* @param 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
|
* @param out is an output stream into which the matching messages are written
|
||||||
* @throws CommunicationException on DB connection error
|
* @throws CommunicationException on DB connection error
|
||||||
*/
|
*/
|
||||||
public void readMessages(MessageFilterList filterList, MessageOutputStream<BulletinBoardMessage> out) throws CommunicationException;
|
public void readMessages(MessageFilterList filterList, MessageOutputStream<BulletinBoardMessage> out) throws CommunicationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Informs server about a new batch message
|
* Return the number of posted messages matching the given filters
|
||||||
* @param message contains the required data about the new batch
|
* @param filterList count only messages that match the filters (empty list or null means no filtering)
|
||||||
* @return TRUE if the batch request is accepted amd FALSE otherwise
|
* @return an IntMsg containing the number of messages that match the filter
|
||||||
* Specifically, if such a batch already exists and is not yet closed: the value returned will be TRUE
|
* @throws CommunicationException on DB connection error
|
||||||
* However, if such a batch exists and is already closed: the value returned will be FALSE
|
*/
|
||||||
* @throws CommunicationException on DB connection error
|
public Int32Value getMessageCount(MessageFilterList filterList) throws CommunicationException;
|
||||||
*/
|
|
||||||
public BoolValue beginBatch(BeginBatchMessage message) throws CommunicationException;
|
/**
|
||||||
|
* Informs server about a new batch message
|
||||||
/**
|
* @param message contains the required data about the new batch
|
||||||
* Posts a (part of a) batch message to the bulletin board
|
* @return a unique batch identifier for the new batch ; -1 if batch creation was unsuccessful
|
||||||
* Note that the existence and contents of a batch message are not available for reading before the batch is finalized
|
* @throws CommunicationException on DB connection error
|
||||||
* @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
|
public Int64Value beginBatch(BeginBatchMessage message) throws CommunicationException;
|
||||||
* @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
|
* Posts a chunk of a batch message to the bulletin board
|
||||||
* @throws CommunicationException on DB connection error
|
* 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
|
||||||
public BoolValue postBatchMessage(BatchMessage batchMessage) throws CommunicationException;
|
* 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
|
||||||
* Attempts to stop and finalize a batch message
|
* However, requiring to open a batch before insertion of messages is implementation-dependent
|
||||||
* @param message contains the data necessary to stop the batch; in particular: the signature for the batch
|
* @throws CommunicationException on DB connection error
|
||||||
* @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
|
public BoolValue postBatchMessage(BatchMessage batchMessage) throws CommunicationException;
|
||||||
* @throws CommunicationException on DB connection error
|
|
||||||
*/
|
/**
|
||||||
public BoolValue closeBatchMessage(CloseBatchMessage message) 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
|
||||||
* Reads a batch message from the server (starting with the supplied position)
|
* Specifically, if the signature is invalid or if some of the batch parts have not yet been submitted: the value returned will be FALSE
|
||||||
* @param message specifies the signer ID and the batch ID to read as well as an (optional) start position
|
* @throws CommunicationException on DB connection error
|
||||||
* @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
|
public BoolValue closeBatch(CloseBatchMessage message) throws CommunicationException;
|
||||||
* @throws IllegalArgumentException if message does not specify a batch
|
|
||||||
*/
|
/**
|
||||||
public void readBatch(BatchSpecificationMessage message, MessageOutputStream<BatchData> out) throws CommunicationException, IllegalArgumentException;
|
* 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)
|
||||||
* Create a SyncQuery to test against that corresponds with the current server state for a specific filter list
|
* @throws CommunicationException on DB connection error
|
||||||
* @param generateSyncQueryParams defines the information needed to generate the query
|
* @throws IllegalArgumentException if message ID does not specify a batch
|
||||||
* @return The generated SyncQuery
|
*/
|
||||||
* @throws CommunicationException on DB connection error
|
public void readBatch(BatchQuery batchQuery, MessageOutputStream<BatchChunk> out) throws CommunicationException, IllegalArgumentException;
|
||||||
*/
|
|
||||||
SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException;
|
/**
|
||||||
|
* 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
|
||||||
* Queries the database for sync status with respect to a given sync query
|
* @return The generated SyncQuery
|
||||||
* @param syncQuery contains a succinct representation of states to compare to
|
* @throws CommunicationException on DB connection error
|
||||||
* @return a SyncQueryResponse object containing the representation of the most recent state the database matches
|
*/
|
||||||
* @throws CommunicationException
|
public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException;
|
||||||
*/
|
|
||||||
public SyncQueryResponse querySync(SyncQuery syncQuery) 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
|
||||||
* This method closes the connection to the DB
|
* @return a SyncQueryResponse object containing the representation of the most recent state the database matches
|
||||||
* @throws CommunicationException on DB connection error
|
* @throws CommunicationException
|
||||||
*/
|
*/
|
||||||
public void close() 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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
|
@ -1,22 +1,84 @@
|
||||||
package meerkat.bulletinboard;
|
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.
|
* Created by Arbel Deutsch Peled on 08-Mar-16.
|
||||||
* This interface defines the behaviour of a bulletin board synchronizer
|
* 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
|
* 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
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Initializes the synchronizer with the required data to function properly
|
||||||
* @param localClient is a client for the local DB instance
|
* @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 DBs
|
* @param remoteClient is a client for the remote servers into which the data needs to be uploaded
|
||||||
* @param minRedundancy
|
|
||||||
*/
|
*/
|
||||||
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<SyncStatus> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the messages which have not yet been synchronized
|
||||||
|
* @return the list of messages remaining to be synchronized
|
||||||
|
*/
|
||||||
|
public List<BulletinBoardMessage> 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<List<BulletinBoardMessage>> 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<Integer> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the synchronization
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run();
|
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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<BatchData> batchDataList;
|
|
||||||
private Signature signature;
|
|
||||||
private Timestamp timestamp;
|
|
||||||
|
|
||||||
public CompleteBatch() {
|
|
||||||
batchDataList = new LinkedList<BatchData>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompleteBatch(BeginBatchMessage newBeginBatchMessage) {
|
|
||||||
this();
|
|
||||||
beginBatchMessage = newBeginBatchMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompleteBatch(BeginBatchMessage newBeginBatchMessage, List<BatchData> newDataList) {
|
|
||||||
this(newBeginBatchMessage);
|
|
||||||
appendBatchData(newDataList);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompleteBatch(BeginBatchMessage newBeginBatchMessage, List<BatchData> newDataList, Signature newSignature) {
|
|
||||||
this(newBeginBatchMessage, newDataList);
|
|
||||||
signature = newSignature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompleteBatch(BeginBatchMessage newBeginBatchMessage, List<BatchData> 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<BatchData> 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<BatchData> 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();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Arbel Deutsch Peled on 13-Apr-16.
|
||||||
|
*/
|
||||||
|
public interface DeletableSubscriptionBulletinBoardClient extends SubscriptionBulletinBoardClient, BulletinBoardMessageDeleter {
|
||||||
|
}
|
|
@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
package meerkat.bulletinboard;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Arbel Deutsch Peled on 03-Mar-16.
|
|
||||||
*/
|
|
||||||
public interface SubscriptionAsyncBulletinBoardClient extends AsyncBulletinBoardClient, BulletinBoardSubscriber {
|
|
||||||
}
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Arbel Deutsch Peled on 03-Mar-16.
|
||||||
|
*/
|
||||||
|
public interface SubscriptionBulletinBoardClient extends AsyncBulletinBoardClient, BulletinBoardSubscriber {
|
||||||
|
}
|
|
@ -1,36 +1,36 @@
|
||||||
package meerkat.comm;
|
package meerkat.comm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by talm on 24/10/15.
|
* Created by talm on 24/10/15.
|
||||||
*/
|
*/
|
||||||
public class CommunicationException extends Exception {
|
public class CommunicationException extends Exception {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generated serial.
|
* Generated serial.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private static final long serialVersionUID = 2279440129497891293L;
|
private static final long serialVersionUID = 2279440129497891293L;
|
||||||
private String message;
|
private String message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor. To be used only if error type is unknown.
|
* Default constructor. To be used only if error type is unknown.
|
||||||
*/
|
*/
|
||||||
public CommunicationException(){
|
public CommunicationException(){
|
||||||
message = "Unknown communication exception";
|
message = "Unknown communication exception";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor enabling specifying of an error message.
|
* Constructor enabling specifying of an error message.
|
||||||
* @param errorMessage
|
* @param errorMessage
|
||||||
*/
|
*/
|
||||||
public CommunicationException(String errorMessage){
|
public CommunicationException(String errorMessage){
|
||||||
message = errorMessage;
|
message = errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the error message specified.
|
* @return the error message specified.
|
||||||
*/
|
*/
|
||||||
public String getMessage(){
|
public String getMessage(){
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,12 @@ public interface Digest {
|
||||||
*/
|
*/
|
||||||
public MessageID digestAsMessageID();
|
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)
|
* Updates the digest using the specified message (in serialized wire form)
|
||||||
*
|
*
|
||||||
|
|
|
@ -39,6 +39,13 @@ public interface DigitalSignature {
|
||||||
*/
|
*/
|
||||||
public void clearVerificationCertificates();
|
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)
|
* Add msg to the content stream to be verified / signed. Each message is (automatically)
|
||||||
|
|
|
@ -140,6 +140,11 @@ public class ECDSASignature implements DigitalSignature {
|
||||||
signer.update(msg.toByteString().asReadOnlyByteBuffer());
|
signer.update(msg.toByteString().asReadOnlyByteBuffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateContent(byte[] data) throws SignatureException {
|
||||||
|
signer.update(data);
|
||||||
|
}
|
||||||
|
|
||||||
public void updateContent(InputStream in) throws IOException, SignatureException {
|
public void updateContent(InputStream in) throws IOException, SignatureException {
|
||||||
ByteString inStr = ByteString.readFrom(in);
|
ByteString inStr = ByteString.readFrom(in);
|
||||||
signer.update(inStr.asReadOnlyByteBuffer());
|
signer.update(inStr.asReadOnlyByteBuffer());
|
||||||
|
|
|
@ -80,6 +80,7 @@ public class SHA256Digest implements Digest {
|
||||||
hash.update(msg.asReadOnlyByteBuffer());
|
hash.update(msg.asReadOnlyByteBuffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
final public void update(byte[] msg) {
|
final public void update(byte[] msg) {
|
||||||
hash.update(msg);
|
hash.update(msg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package meerkat.logging;
|
package meerkat.logging;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by talm on 25/10/15.
|
* Created by talm on 25/10/15.
|
||||||
*/
|
*/
|
||||||
public class LogVerifier {
|
public class LogVerifier {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package meerkat.logging;
|
package meerkat.logging;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by talm on 25/10/15.
|
* Created by talm on 25/10/15.
|
||||||
*/
|
*/
|
||||||
public class Logger {
|
public class Logger {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +1,83 @@
|
||||||
package meerkat.util;
|
package meerkat.util;
|
||||||
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI;
|
import meerkat.protobuf.BulletinBoardAPI;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.protobuf.Crypto.*;
|
import meerkat.protobuf.Crypto.*;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
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:
|
* Created by Arbel Deutsch Peled on 05-Dec-15.
|
||||||
* 1. The entry number (since this can be different between database instances)
|
* This class implements a comparison between BulletinBoardMessage instances that disregards:
|
||||||
* 2. The order of the signatures
|
* 1. The entry number (since this can be different between database instances)
|
||||||
*/
|
* 2. The order of the signatures
|
||||||
public class BulletinBoardMessageComparator implements Comparator<BulletinBoardMessage> {
|
*/
|
||||||
|
public class BulletinBoardMessageComparator implements Comparator<BulletinBoardMessage> {
|
||||||
/**
|
|
||||||
* Compare the messages
|
/**
|
||||||
* @param msg1
|
* Compare the messages
|
||||||
* @param msg2
|
* @param msg1
|
||||||
* @return 0 if the messages are equivalent (see above) and -1 otherwise.
|
* @param msg2
|
||||||
*/
|
* @return 0 if the messages are equivalent (see above) and -1 otherwise.
|
||||||
@Override
|
*/
|
||||||
public int compare(BulletinBoardMessage msg1, BulletinBoardMessage msg2) {
|
@Override
|
||||||
|
public int compare(BulletinBoardMessage msg1, BulletinBoardMessage msg2) {
|
||||||
List<Signature> msg1Sigs = msg1.getSigList();
|
|
||||||
List<Signature> msg2Sigs = msg2.getSigList();
|
|
||||||
|
|
||||||
// Compare unsigned message
|
// Compare Timestamps
|
||||||
if (!msg1.getMsg().equals(msg2.getMsg())){
|
|
||||||
return -1;
|
if (!msg1.getMsg().getTimestamp().equals(msg2.getMsg().getTimestamp())){
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
// Compare signatures
|
|
||||||
|
// Compare tags (enforce order)
|
||||||
if (msg1Sigs.size() != msg2Sigs.size()){
|
|
||||||
return -1;
|
List<String> tags1 = msg1.getMsg().getTagList();
|
||||||
}
|
Iterator<String> tags2 = msg2.getMsg().getTagList().iterator();
|
||||||
|
|
||||||
for (Signature sig : msg1Sigs){
|
for (String tag : tags1){
|
||||||
if (!msg2Sigs.contains(sig)) {
|
if (!tags2.hasNext()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
if (!tags2.next().equals(tag)){
|
||||||
|
return -1;
|
||||||
return 0;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// 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<Signature> sigs1 = msg1.getSigList();
|
||||||
|
List<Signature> sigs2 = msg2.getSigList();
|
||||||
|
|
||||||
|
if (sigs1.size() != sigs2.size()){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Signature sig : sigs1){
|
||||||
|
if (!sigs2.contains(sig)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue