Merge branch 'master' into vbdev2
Conflicts: meerkat-common/src/main/proto/meerkat/voting.protovbdev2
commit
d12ad408c4
|
@ -1,16 +1,16 @@
|
|||
.gradle
|
||||
.idea
|
||||
build
|
||||
bin
|
||||
.settings
|
||||
.classpath
|
||||
.project
|
||||
out
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
**/*.swp
|
||||
*.prefs
|
||||
*.project
|
||||
*.classpath
|
||||
bulletin-board-server/local-instances/meerkat.db
|
||||
.gradle
|
||||
.idea
|
||||
build
|
||||
bin
|
||||
.settings
|
||||
.classpath
|
||||
.project
|
||||
out
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
**/*.swp
|
||||
*.prefs
|
||||
*.project
|
||||
*.classpath
|
||||
bulletin-board-server/local-instances/meerkat.db
|
||||
|
|
20
build.gradle
20
build.gradle
|
@ -1,10 +1,10 @@
|
|||
|
||||
subprojects { proj ->
|
||||
proj.afterEvaluate {
|
||||
// Used to generate initial maven-dir layout
|
||||
task "create-dirs" { description = "Create default maven directory structure" } << {
|
||||
sourceSets*.java.srcDirs*.each { it.mkdirs() }
|
||||
sourceSets*.resources.srcDirs*.each { it.mkdirs() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subprojects { proj ->
|
||||
proj.afterEvaluate {
|
||||
// Used to generate initial maven-dir layout
|
||||
task "create-dirs" { description = "Create default maven directory structure" } << {
|
||||
sourceSets*.java.srcDirs*.each { it.mkdirs() }
|
||||
sourceSets*.resources.srcDirs*.each { it.mkdirs() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,220 +1,220 @@
|
|||
|
||||
plugins {
|
||||
id "us.kirchmeier.capsule" version "1.0.1"
|
||||
id 'com.google.protobuf' version '0.7.0'
|
||||
}
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'idea'
|
||||
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
// Uncomment the lines below to define an application
|
||||
// (this will also allow you to build a "fatCapsule" which includes
|
||||
// the entire application, including all dependencies in a single jar)
|
||||
//apply plugin: 'application'
|
||||
//mainClassName='your.main.ApplicationClass'
|
||||
|
||||
// Is this a snapshot version?
|
||||
ext { isSnapshot = false }
|
||||
|
||||
ext {
|
||||
groupId = 'org.factcenter.meerkat'
|
||||
nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/"
|
||||
|
||||
// Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing)
|
||||
// Should be set in ${HOME}/.gradle/gradle.properties
|
||||
nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : ""
|
||||
nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : ""
|
||||
}
|
||||
|
||||
description = "TODO: Add a description"
|
||||
|
||||
// Your project version
|
||||
version = "0.0"
|
||||
|
||||
version += "${isSnapshot ? '-SNAPSHOT' : ''}"
|
||||
|
||||
|
||||
dependencies {
|
||||
// Meerkat common
|
||||
compile project(':meerkat-common')
|
||||
|
||||
// Logging
|
||||
compile 'org.slf4j:slf4j-api:1.7.7'
|
||||
runtime 'ch.qos.logback:logback-classic:1.1.2'
|
||||
runtime 'ch.qos.logback:logback-core:1.1.2'
|
||||
|
||||
// Google protobufs
|
||||
compile 'com.google.protobuf:protobuf-java:3.+'
|
||||
|
||||
testCompile 'junit:junit:4.+'
|
||||
|
||||
runtime 'org.codehaus.groovy:groovy:2.4.+'
|
||||
}
|
||||
|
||||
|
||||
/*==== You probably don't have to edit below this line =======*/
|
||||
|
||||
// Setup test configuration that can appear as a dependency in
|
||||
// other subprojects
|
||||
configurations {
|
||||
testOutput.extendsFrom (testCompile)
|
||||
}
|
||||
|
||||
task testJar(type: Jar, dependsOn: testClasses) {
|
||||
classifier = 'tests'
|
||||
from sourceSets.test.output
|
||||
}
|
||||
|
||||
artifacts {
|
||||
testOutput testJar
|
||||
}
|
||||
|
||||
// The run task added by the application plugin
|
||||
// is also of type JavaExec.
|
||||
tasks.withType(JavaExec) {
|
||||
// Assign all Java system properties from
|
||||
// the command line to the JavaExec task.
|
||||
systemProperties System.properties
|
||||
}
|
||||
|
||||
|
||||
protobuf {
|
||||
// Configure the protoc executable
|
||||
protoc {
|
||||
// Download from repositories
|
||||
artifact = 'com.google.protobuf:protoc:3.+'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
idea {
|
||||
module {
|
||||
project.sourceSets.each { sourceSet ->
|
||||
|
||||
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
||||
|
||||
println "Adding $srcDir"
|
||||
// add protobuf generated sources to generated source dir.
|
||||
if ("test".equals(sourceSet.name)) {
|
||||
testSourceDirs += file(srcDir)
|
||||
} else {
|
||||
sourceDirs += file(srcDir)
|
||||
}
|
||||
generatedSourceDirs += file(srcDir)
|
||||
|
||||
}
|
||||
|
||||
// Don't exclude build directory
|
||||
excludeDirs -= file(buildDir)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*===================================
|
||||
* "Fat" Build targets
|
||||
*===================================*/
|
||||
|
||||
|
||||
if (project.hasProperty('mainClassName') && (mainClassName != null)) {
|
||||
|
||||
task mavenCapsule(type: MavenCapsule) {
|
||||
description = "Generate a capsule jar that automatically downloads and caches dependencies when run."
|
||||
applicationClass mainClassName
|
||||
destinationDir = buildDir
|
||||
}
|
||||
|
||||
task fatCapsule(type: FatCapsule) {
|
||||
description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class"
|
||||
|
||||
destinationDir = buildDir
|
||||
|
||||
def fatMain = hasProperty('fatmain') ? fatmain : mainClassName
|
||||
|
||||
applicationClass fatMain
|
||||
|
||||
def testJar = hasProperty('test')
|
||||
|
||||
if (hasProperty('fatmain')) {
|
||||
appendix = "fat-${fatMain}"
|
||||
} else {
|
||||
appendix = "fat"
|
||||
}
|
||||
|
||||
if (testJar) {
|
||||
from sourceSets.test.output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*===================================
|
||||
* Repositories
|
||||
*===================================*/
|
||||
|
||||
repositories {
|
||||
|
||||
// Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral)
|
||||
maven {
|
||||
url nexusRepository
|
||||
|
||||
if (isSnapshot) {
|
||||
credentials { username
|
||||
password
|
||||
|
||||
username nexusUser
|
||||
password nexusPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use local maven repository
|
||||
mavenLocal()
|
||||
|
||||
// Use 'maven central' for other dependencies.
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
task "info" << {
|
||||
println "Project: ${project.name}"
|
||||
println "Description: ${project.description}"
|
||||
println "--------------------------"
|
||||
println "GroupId: $groupId"
|
||||
println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})"
|
||||
println ""
|
||||
}
|
||||
info.description 'Print some information about project parameters'
|
||||
|
||||
|
||||
/*===================================
|
||||
* Publishing
|
||||
*===================================*/
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
groupId project.groupId
|
||||
pom.withXml {
|
||||
asNode().appendNode('description', project.description)
|
||||
}
|
||||
from project.components.java
|
||||
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}"
|
||||
credentials { username
|
||||
password
|
||||
|
||||
username nexusUser
|
||||
password nexusPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
plugins {
|
||||
id "us.kirchmeier.capsule" version "1.0.1"
|
||||
id 'com.google.protobuf' version '0.7.0'
|
||||
}
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'idea'
|
||||
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
// Uncomment the lines below to define an application
|
||||
// (this will also allow you to build a "fatCapsule" which includes
|
||||
// the entire application, including all dependencies in a single jar)
|
||||
//apply plugin: 'application'
|
||||
//mainClassName='your.main.ApplicationClass'
|
||||
|
||||
// Is this a snapshot version?
|
||||
ext { isSnapshot = false }
|
||||
|
||||
ext {
|
||||
groupId = 'org.factcenter.meerkat'
|
||||
nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/"
|
||||
|
||||
// Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing)
|
||||
// Should be set in ${HOME}/.gradle/gradle.properties
|
||||
nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : ""
|
||||
nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : ""
|
||||
}
|
||||
|
||||
description = "TODO: Add a description"
|
||||
|
||||
// Your project version
|
||||
version = "0.0"
|
||||
|
||||
version += "${isSnapshot ? '-SNAPSHOT' : ''}"
|
||||
|
||||
|
||||
dependencies {
|
||||
// Meerkat common
|
||||
compile project(':meerkat-common')
|
||||
|
||||
// Logging
|
||||
compile 'org.slf4j:slf4j-api:1.7.7'
|
||||
runtime 'ch.qos.logback:logback-classic:1.1.2'
|
||||
runtime 'ch.qos.logback:logback-core:1.1.2'
|
||||
|
||||
// Google protobufs
|
||||
compile 'com.google.protobuf:protobuf-java:3.+'
|
||||
|
||||
testCompile 'junit:junit:4.+'
|
||||
|
||||
runtime 'org.codehaus.groovy:groovy:2.4.+'
|
||||
}
|
||||
|
||||
|
||||
/*==== You probably don't have to edit below this line =======*/
|
||||
|
||||
// Setup test configuration that can appear as a dependency in
|
||||
// other subprojects
|
||||
configurations {
|
||||
testOutput.extendsFrom (testCompile)
|
||||
}
|
||||
|
||||
task testJar(type: Jar, dependsOn: testClasses) {
|
||||
classifier = 'tests'
|
||||
from sourceSets.test.output
|
||||
}
|
||||
|
||||
artifacts {
|
||||
testOutput testJar
|
||||
}
|
||||
|
||||
// The run task added by the application plugin
|
||||
// is also of type JavaExec.
|
||||
tasks.withType(JavaExec) {
|
||||
// Assign all Java system properties from
|
||||
// the command line to the JavaExec task.
|
||||
systemProperties System.properties
|
||||
}
|
||||
|
||||
|
||||
protobuf {
|
||||
// Configure the protoc executable
|
||||
protoc {
|
||||
// Download from repositories
|
||||
artifact = 'com.google.protobuf:protoc:3.+'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
idea {
|
||||
module {
|
||||
project.sourceSets.each { sourceSet ->
|
||||
|
||||
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
||||
|
||||
println "Adding $srcDir"
|
||||
// add protobuf generated sources to generated source dir.
|
||||
if ("test".equals(sourceSet.name)) {
|
||||
testSourceDirs += file(srcDir)
|
||||
} else {
|
||||
sourceDirs += file(srcDir)
|
||||
}
|
||||
generatedSourceDirs += file(srcDir)
|
||||
|
||||
}
|
||||
|
||||
// Don't exclude build directory
|
||||
excludeDirs -= file(buildDir)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*===================================
|
||||
* "Fat" Build targets
|
||||
*===================================*/
|
||||
|
||||
|
||||
if (project.hasProperty('mainClassName') && (mainClassName != null)) {
|
||||
|
||||
task mavenCapsule(type: MavenCapsule) {
|
||||
description = "Generate a capsule jar that automatically downloads and caches dependencies when run."
|
||||
applicationClass mainClassName
|
||||
destinationDir = buildDir
|
||||
}
|
||||
|
||||
task fatCapsule(type: FatCapsule) {
|
||||
description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class"
|
||||
|
||||
destinationDir = buildDir
|
||||
|
||||
def fatMain = hasProperty('fatmain') ? fatmain : mainClassName
|
||||
|
||||
applicationClass fatMain
|
||||
|
||||
def testJar = hasProperty('test')
|
||||
|
||||
if (hasProperty('fatmain')) {
|
||||
appendix = "fat-${fatMain}"
|
||||
} else {
|
||||
appendix = "fat"
|
||||
}
|
||||
|
||||
if (testJar) {
|
||||
from sourceSets.test.output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*===================================
|
||||
* Repositories
|
||||
*===================================*/
|
||||
|
||||
repositories {
|
||||
|
||||
// Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral)
|
||||
maven {
|
||||
url nexusRepository
|
||||
|
||||
if (isSnapshot) {
|
||||
credentials { username
|
||||
password
|
||||
|
||||
username nexusUser
|
||||
password nexusPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use local maven repository
|
||||
mavenLocal()
|
||||
|
||||
// Use 'maven central' for other dependencies.
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
task "info" << {
|
||||
println "Project: ${project.name}"
|
||||
println "Description: ${project.description}"
|
||||
println "--------------------------"
|
||||
println "GroupId: $groupId"
|
||||
println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})"
|
||||
println ""
|
||||
}
|
||||
info.description 'Print some information about project parameters'
|
||||
|
||||
|
||||
/*===================================
|
||||
* Publishing
|
||||
*===================================*/
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
groupId project.groupId
|
||||
pom.withXml {
|
||||
asNode().appendNode('description', project.description)
|
||||
}
|
||||
from project.components.java
|
||||
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}"
|
||||
credentials { username
|
||||
password
|
||||
|
||||
username nexusUser
|
||||
password nexusPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
|
||||
plugins {
|
||||
id "us.kirchmeier.capsule" version "1.0.1"
|
||||
id 'com.google.protobuf' version '0.7.0'
|
||||
}
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'com.google.protobuf'
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'idea'
|
||||
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
// Is this a snapshot version?
|
||||
ext { isSnapshot = false }
|
||||
|
||||
ext {
|
||||
groupId = 'org.factcenter.meerkat'
|
||||
nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/"
|
||||
|
||||
// Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing)
|
||||
// Should be set in ${HOME}/.gradle/gradle.properties
|
||||
nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : ""
|
||||
nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : ""
|
||||
}
|
||||
|
||||
description = "Meerkat Voting Common Library"
|
||||
|
||||
// Your project version
|
||||
version = "0.0"
|
||||
|
||||
version += "${isSnapshot ? '-SNAPSHOT' : ''}"
|
||||
|
||||
|
||||
dependencies {
|
||||
|
||||
// Meerkat common
|
||||
compile project(':meerkat-common')
|
||||
compile project(':restful-api-common')
|
||||
|
||||
// Jersey for RESTful API
|
||||
compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.22.+'
|
||||
compile 'org.xerial:sqlite-jdbc:3.7.+'
|
||||
|
||||
// Logging
|
||||
compile 'org.slf4j:slf4j-api:1.7.7'
|
||||
runtime 'ch.qos.logback:logback-classic:1.1.2'
|
||||
runtime 'ch.qos.logback:logback-core:1.1.2'
|
||||
|
||||
// Google protobufs
|
||||
compile 'com.google.protobuf:protobuf-java:3.+'
|
||||
|
||||
// Crypto
|
||||
compile 'org.factcenter.qilin:qilin:1.1+'
|
||||
compile 'org.bouncycastle:bcprov-jdk15on:1.53'
|
||||
compile 'org.bouncycastle:bcpkix-jdk15on:1.53'
|
||||
|
||||
// Depend on test resources from meerkat-common
|
||||
testCompile project(path: ':meerkat-common', configuration: 'testOutput')
|
||||
|
||||
// Depend on server compilation for the non-integration tests
|
||||
testCompile project(path: ':bulletin-board-server')
|
||||
|
||||
testCompile 'junit:junit:4.+'
|
||||
testCompile 'org.hamcrest:hamcrest-all:1.3'
|
||||
|
||||
runtime 'org.codehaus.groovy:groovy:2.4.+'
|
||||
}
|
||||
|
||||
test {
|
||||
exclude '**/*IntegrationTest*'
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
|
||||
task integrationTest(type: Test) {
|
||||
include '**/*IntegrationTest*'
|
||||
// debug = true
|
||||
outputs.upToDateWhen { false }
|
||||
|
||||
}
|
||||
|
||||
/*==== You probably don't have to edit below this line =======*/
|
||||
|
||||
|
||||
// Setup test configuration that can appear as a dependency in
|
||||
// other subprojects
|
||||
configurations {
|
||||
testOutput.extendsFrom (testCompile)
|
||||
}
|
||||
|
||||
task testJar(type: Jar, dependsOn: testClasses) {
|
||||
classifier = 'tests'
|
||||
from sourceSets.test.output
|
||||
}
|
||||
|
||||
artifacts {
|
||||
testOutput testJar
|
||||
}
|
||||
|
||||
|
||||
|
||||
// The run task added by the application plugin
|
||||
// is also of type JavaExec.
|
||||
tasks.withType(JavaExec) {
|
||||
// Assign all Java system properties from
|
||||
// the command line to the JavaExec task.
|
||||
systemProperties System.properties
|
||||
}
|
||||
|
||||
|
||||
protobuf {
|
||||
// Configure the protoc executable
|
||||
protoc {
|
||||
// Download from repositories
|
||||
artifact = 'com.google.protobuf:protoc:3.+'
|
||||
}
|
||||
}
|
||||
|
||||
idea {
|
||||
module {
|
||||
project.sourceSets.each { sourceSet ->
|
||||
|
||||
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
||||
|
||||
// add protobuf generated sources to generated source dir.
|
||||
if ("test".equals(sourceSet.name)) {
|
||||
testSourceDirs += file(srcDir)
|
||||
} else {
|
||||
sourceDirs += file(srcDir)
|
||||
}
|
||||
generatedSourceDirs += file(srcDir)
|
||||
|
||||
}
|
||||
|
||||
// Don't exclude build directory
|
||||
excludeDirs -= file(buildDir)
|
||||
}
|
||||
}
|
||||
|
||||
/*===================================
|
||||
* "Fat" Build targets
|
||||
*===================================*/
|
||||
|
||||
if (project.hasProperty('mainClassName') && (mainClassName != null)) {
|
||||
|
||||
task mavenCapsule(type: MavenCapsule) {
|
||||
description = "Generate a capsule jar that automatically downloads and caches dependencies when run."
|
||||
applicationClass mainClassName
|
||||
destinationDir = buildDir
|
||||
}
|
||||
|
||||
task fatCapsule(type: FatCapsule) {
|
||||
description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class"
|
||||
|
||||
destinationDir = buildDir
|
||||
|
||||
def fatMain = hasProperty('fatmain') ? fatmain : mainClassName
|
||||
|
||||
applicationClass fatMain
|
||||
|
||||
def testJar = hasProperty('test')
|
||||
|
||||
if (hasProperty('fatmain')) {
|
||||
appendix = "fat-${fatMain}"
|
||||
} else {
|
||||
appendix = "fat"
|
||||
}
|
||||
|
||||
if (testJar) {
|
||||
from sourceSets.test.output
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*===================================
|
||||
* Repositories
|
||||
*===================================*/
|
||||
|
||||
repositories {
|
||||
|
||||
mavenLocal();
|
||||
|
||||
// Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral)
|
||||
maven {
|
||||
url nexusRepository
|
||||
|
||||
if (isSnapshot) {
|
||||
credentials { username
|
||||
password
|
||||
|
||||
username nexusUser
|
||||
password nexusPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use 'maven central' for other dependencies.
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
task "info" << {
|
||||
println "Project: ${project.name}"
|
||||
println "Description: ${project.description}"
|
||||
println "--------------------------"
|
||||
println "GroupId: $groupId"
|
||||
println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})"
|
||||
println ""
|
||||
}
|
||||
info.description 'Print some information about project parameters'
|
||||
|
||||
|
||||
/*===================================
|
||||
* Publishing
|
||||
*===================================*/
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
groupId project.groupId
|
||||
pom.withXml {
|
||||
asNode().appendNode('description', project.description)
|
||||
}
|
||||
from project.components.java
|
||||
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}"
|
||||
credentials { username
|
||||
password
|
||||
|
||||
username nexusUser
|
||||
password nexusPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BatchData;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 17-Jan-16.
|
||||
* Used to store the complete data required for sending a batch data list inside a single object
|
||||
*/
|
||||
public class BatchDataContainer {
|
||||
|
||||
public final byte[] signerId;
|
||||
public final int batchId;
|
||||
public final List<BatchData> batchDataList;
|
||||
public final int startPosition;
|
||||
|
||||
public BatchDataContainer(byte[] signerId, int batchId, List<BatchData> batchDataList, int startPosition) {
|
||||
this.signerId = signerId;
|
||||
this.batchId = batchId;
|
||||
this.batchDataList = batchDataList;
|
||||
this.startPosition = startPosition;
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||
*
|
||||
* This class specifies the job that is required of a Bulletin Board Client Worker
|
||||
*/
|
||||
public class BulletinClientJob {
|
||||
|
||||
public static enum JobType{
|
||||
POST_MESSAGE, // Post a message to servers
|
||||
READ_MESSAGES, // Read messages according to some given filter (any server will do)
|
||||
GET_REDUNDANCY // Check the redundancy of a specific message in the databases
|
||||
}
|
||||
|
||||
private List<String> serverAddresses;
|
||||
|
||||
private int minServers; // The minimal number of servers the job must be successful on for the job to be completed
|
||||
|
||||
private final JobType jobType;
|
||||
|
||||
private final Message payload; // The information associated with the job type
|
||||
|
||||
private int maxRetry; // Number of retries for this job; set to -1 for infinite retries
|
||||
|
||||
public BulletinClientJob(List<String> serverAddresses, int minServers, JobType jobType, Message payload, int maxRetry) {
|
||||
this.serverAddresses = serverAddresses;
|
||||
this.minServers = minServers;
|
||||
this.jobType = jobType;
|
||||
this.payload = payload;
|
||||
this.maxRetry = maxRetry;
|
||||
}
|
||||
|
||||
public void updateServerAddresses(List<String> newServerAdresses) {
|
||||
this.serverAddresses = newServerAdresses;
|
||||
}
|
||||
|
||||
public List<String> getServerAddresses() {
|
||||
return serverAddresses;
|
||||
}
|
||||
|
||||
public int getMinServers() {
|
||||
return minServers;
|
||||
}
|
||||
|
||||
public JobType getJobType() {
|
||||
return jobType;
|
||||
}
|
||||
|
||||
public Message getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public int getMaxRetry() {
|
||||
return maxRetry;
|
||||
}
|
||||
|
||||
public void shuffleAddresses() {
|
||||
Collections.shuffle(serverAddresses);
|
||||
}
|
||||
|
||||
public void decMinServers(){
|
||||
minServers--;
|
||||
}
|
||||
|
||||
public void decMaxRetry(){
|
||||
if (maxRetry > 0) {
|
||||
maxRetry--;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRetry(){
|
||||
return (maxRetry != 0);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||
*
|
||||
* This class contains the end status and result of a Bulletin Board Client Job.
|
||||
*/
|
||||
public final class BulletinClientJobResult {
|
||||
|
||||
private final BulletinClientJob job; // Stores the job the result refers to
|
||||
|
||||
private final Message result; // The result of the job; valid only if success==true
|
||||
|
||||
public BulletinClientJobResult(BulletinClientJob job, Message result) {
|
||||
this.job = job;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public BulletinClientJob getJob() {
|
||||
return job;
|
||||
}
|
||||
|
||||
public Message getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,217 +1,38 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.crypto.Digest;
|
||||
import meerkat.crypto.concrete.SHA256Digest;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.rest.Constants;
|
||||
import meerkat.rest.ProtobufMessageBodyReader;
|
||||
import meerkat.rest.ProtobufMessageBodyWriter;
|
||||
|
||||
import javax.ws.rs.ProcessingException;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||
*
|
||||
* This class implements the actual communication with the Bulletin Board Servers.
|
||||
* It is meant to be used in a multi-threaded environment.
|
||||
*/
|
||||
//TODO: Maybe make this abstract and inherit from it.
|
||||
public class BulletinClientWorker implements Callable<BulletinClientJobResult> {
|
||||
|
||||
private final BulletinClientJob job; // The requested job to be handled
|
||||
|
||||
public BulletinClientWorker(BulletinClientJob job){
|
||||
this.job = job;
|
||||
}
|
||||
|
||||
// This resource enabled creation of a single Client per thread.
|
||||
private static final ThreadLocal<Client> clientLocal =
|
||||
new ThreadLocal<Client> () {
|
||||
@Override protected Client initialValue() {
|
||||
Client client;
|
||||
client = ClientBuilder.newClient();
|
||||
client.register(ProtobufMessageBodyReader.class);
|
||||
client.register(ProtobufMessageBodyWriter.class);
|
||||
|
||||
return client;
|
||||
}
|
||||
};
|
||||
|
||||
// This resource enables creation of a single Digest per thread.
|
||||
private static final ThreadLocal<Digest> digestLocal =
|
||||
new ThreadLocal<Digest> () {
|
||||
@Override protected Digest initialValue() {
|
||||
Digest digest;
|
||||
digest = new SHA256Digest(); //TODO: Make this generic.
|
||||
|
||||
return digest;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This method carries out the actual communication with the servers via HTTP Post
|
||||
* It accesses the servers according to the job it received and updates said job as it goes
|
||||
* The method will only iterate once through the server list, removing servers from the list when they are no longer required
|
||||
* In a POST_MESSAGE job: successful post to a server results in removing the server from the list
|
||||
* In a GET_REDUNDANCY job: no server is removed from the list and the (absolute) number of servers in which the message was found is returned
|
||||
* In a READ_MESSAGES job: successful retrieval from any server terminates the method and returns the received values; The list is not changed
|
||||
* @return The original job, modified to fit the current state and the required output (if any) of the operation
|
||||
* @throws IllegalArgumentException
|
||||
* @throws CommunicationException
|
||||
*/
|
||||
public BulletinClientJobResult call() throws IllegalArgumentException, CommunicationException{
|
||||
|
||||
Client client = clientLocal.get();
|
||||
Digest digest = digestLocal.get();
|
||||
|
||||
WebTarget webTarget;
|
||||
Response response;
|
||||
|
||||
String requestPath;
|
||||
Message msg;
|
||||
|
||||
List<String> serverAddresses = new LinkedList<String>(job.getServerAddresses());
|
||||
|
||||
Message payload = job.getPayload();
|
||||
|
||||
BulletinBoardMessageList msgList;
|
||||
|
||||
int count = 0; // Used to count number of servers which contain the required message in a GET_REDUNDANCY request.
|
||||
|
||||
job.shuffleAddresses(); // This is done to randomize the order of access to servers primarily for READ operations
|
||||
|
||||
// Prepare the request.
|
||||
switch(job.getJobType()) {
|
||||
|
||||
case POST_MESSAGE:
|
||||
// Make sure the payload is a BulletinBoardMessage
|
||||
if (!(payload instanceof BulletinBoardMessage)) {
|
||||
throw new IllegalArgumentException("Cannot post an object that is not an instance of BulletinBoardMessage");
|
||||
}
|
||||
|
||||
msg = payload;
|
||||
requestPath = Constants.POST_MESSAGE_PATH;
|
||||
break;
|
||||
|
||||
case READ_MESSAGES:
|
||||
// Make sure the payload is a MessageFilterList
|
||||
if (!(payload instanceof MessageFilterList)) {
|
||||
throw new IllegalArgumentException("Read failed: an instance of MessageFilterList is required as payload for a READ_MESSAGES operation");
|
||||
}
|
||||
|
||||
msg = payload;
|
||||
requestPath = Constants.READ_MESSAGES_PATH;
|
||||
break;
|
||||
|
||||
case GET_REDUNDANCY:
|
||||
// Make sure the payload is a MessageId
|
||||
if (!(payload instanceof MessageID)) {
|
||||
throw new IllegalArgumentException("Cannot search for an object that is not an instance of MessageID");
|
||||
}
|
||||
|
||||
requestPath = Constants.READ_MESSAGES_PATH;
|
||||
|
||||
msg = MessageFilterList.newBuilder()
|
||||
.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.MSG_ID)
|
||||
.setId(((MessageID) payload).getID())
|
||||
.build()
|
||||
).build();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported job type");
|
||||
|
||||
}
|
||||
|
||||
// Iterate through servers
|
||||
|
||||
Iterator<String> addressIterator = serverAddresses.iterator();
|
||||
|
||||
while (addressIterator.hasNext()) {
|
||||
|
||||
// Send request to Server
|
||||
String address = addressIterator.next();
|
||||
webTarget = client.target(address).path(Constants.BULLETIN_BOARD_SERVER_PATH).path(requestPath);
|
||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
||||
|
||||
// Retrieve answer
|
||||
switch(job.getJobType()) {
|
||||
|
||||
case POST_MESSAGE:
|
||||
try {
|
||||
|
||||
response.readEntity(BoolMsg.class); // If a BoolMsg entity is returned: the post was successful
|
||||
addressIterator.remove(); // Post to this server succeeded: remove server from list
|
||||
job.decMinServers();
|
||||
|
||||
} catch (ProcessingException | IllegalStateException e) {} // Post to this server failed: retry next time
|
||||
finally {
|
||||
response.close();
|
||||
}
|
||||
break;
|
||||
|
||||
case GET_REDUNDANCY:
|
||||
try {
|
||||
msgList = response.readEntity(BulletinBoardMessageList.class); // If a BulletinBoardMessageList is returned: the read was successful
|
||||
|
||||
if (msgList.getMessageList().size() > 0){ // Message was found in the server.
|
||||
count++;
|
||||
}
|
||||
} catch (ProcessingException | IllegalStateException e) {} // Read failed: try with next server
|
||||
finally {
|
||||
response.close();
|
||||
}
|
||||
break;
|
||||
|
||||
case READ_MESSAGES:
|
||||
try {
|
||||
msgList = response.readEntity(BulletinBoardMessageList.class); // If a BulletinBoardMessageList is returned: the read was successful
|
||||
return new BulletinClientJobResult(job, msgList); // Return the result
|
||||
} catch (ProcessingException | IllegalStateException e) {} // Read failed: try with next server
|
||||
finally {
|
||||
response.close();
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Return result (if haven't done so yet)
|
||||
switch(job.getJobType()) {
|
||||
|
||||
case POST_MESSAGE:
|
||||
// The job now contains the information required to ascertain whether enough server posts have succeeded
|
||||
// It will also contain the list of servers in which the post was not successful
|
||||
job.updateServerAddresses(serverAddresses);
|
||||
return new BulletinClientJobResult(job, null);
|
||||
|
||||
case GET_REDUNDANCY:
|
||||
// Return the number of servers in which the message was found
|
||||
// The job now contains the list of these servers
|
||||
return new BulletinClientJobResult(job, IntMsg.newBuilder().setValue(count).build());
|
||||
|
||||
case READ_MESSAGES:
|
||||
// A successful operation would have already returned an output
|
||||
// Therefore: no server access was successful
|
||||
throw new CommunicationException("Could not access any server");
|
||||
|
||||
default: // This is required for successful compilation
|
||||
throw new IllegalArgumentException("Unsupported job type");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
package meerkat.bulletinboard;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||
*
|
||||
* This class handles bulletin client work.
|
||||
* It is meant to be used in a multi-threaded environment.
|
||||
*/
|
||||
public abstract class BulletinClientWorker<IN> {
|
||||
|
||||
protected final IN payload; // Payload of the job
|
||||
|
||||
private int maxRetry; // Number of retries for this job; set to -1 for infinite retries
|
||||
|
||||
public BulletinClientWorker(IN payload, int maxRetry) {
|
||||
this.payload = payload;
|
||||
this.maxRetry = maxRetry;
|
||||
}
|
||||
|
||||
public IN getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public int getMaxRetry() {
|
||||
return maxRetry;
|
||||
}
|
||||
|
||||
public void decMaxRetry(){
|
||||
if (maxRetry > 0) {
|
||||
maxRetry--;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRetry(){
|
||||
return (maxRetry != 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.protobuf.BulletinBoardAPI;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.protobuf.Voting.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 03-Mar-16.
|
||||
* This is a full-fledged implementation of a Bulletin Board Client
|
||||
* It provides asynchronous access to several remote servers, as well as a local cache
|
||||
* Read/write operations are performed on the local server
|
||||
* After any read is carried out, a subscription is made for the specific query to make sure the local DB will be updated
|
||||
* The database also employs a synchronizer which makes sure local data is sent to the remote servers
|
||||
*/
|
||||
public class CachedBulletinBoardClient implements SubscriptionAsyncBulletinBoardClient {
|
||||
|
||||
private final BulletinBoardClient localClient;
|
||||
private AsyncBulletinBoardClient remoteClient;
|
||||
private BulletinBoardSubscriber subscriber;
|
||||
|
||||
private final int threadPoolSize;
|
||||
private final long failDelayInMilliseconds;
|
||||
private final long subscriptionIntervalInMilliseconds;
|
||||
|
||||
public CachedBulletinBoardClient(BulletinBoardClient localClient,
|
||||
int threadPoolSize,
|
||||
long failDelayInMilliseconds,
|
||||
long subscriptionIntervalInMilliseconds)
|
||||
throws IllegalAccessException, InstantiationException {
|
||||
|
||||
this.localClient = localClient;
|
||||
this.threadPoolSize = threadPoolSize;
|
||||
this.failDelayInMilliseconds = failDelayInMilliseconds;
|
||||
this.subscriptionIntervalInMilliseconds = subscriptionIntervalInMilliseconds;
|
||||
|
||||
remoteClient = new ThreadedBulletinBoardClient();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageID postMessage(BulletinBoardMessage msg, FutureCallback<Boolean> callback) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageID postBatch(CompleteBatch completeBatch, FutureCallback<Boolean> callback) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback<Boolean> callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, int startPosition, FutureCallback<Boolean> callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, int startPosition, FutureCallback<Boolean> callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback<Boolean> callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getRedundancy(MessageID id, FutureCallback<Float> callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback<CompleteBatch> callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void querySync(SyncQuery syncQuery, FutureCallback<SyncQueryResponse> callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(BulletinBoardClientParams clientParams) {
|
||||
|
||||
remoteClient.init(clientParams);
|
||||
|
||||
ListeningScheduledExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(threadPoolSize));
|
||||
|
||||
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
|
||||
public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getRedundancy(MessageID id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SyncQuery generateSyncQuery(GenerateSyncQueryParams GenerateSyncQueryParams) throws CommunicationException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(MessageFilterList filterList, long startEntry, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,531 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.common.util.concurrent.*;
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.comm.MessageInputStream;
|
||||
import meerkat.comm.MessageInputStream.MessageInputStreamFactory;
|
||||
import meerkat.comm.MessageOutputStream;
|
||||
import meerkat.crypto.concrete.SHA256Digest;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.protobuf.Voting.*;
|
||||
import meerkat.util.BulletinBoardUtils;
|
||||
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 15-Mar-16.
|
||||
* This client is to be used mainly for testing.
|
||||
* It wraps a BulletinBoardServer in an asynchronous client.
|
||||
* This means the access to the server is direct (via method calls) instead of through a TCP connection.
|
||||
* The client implements both synchronous and asynchronous method calls, but calls to the server itself are performed synchronously.
|
||||
*/
|
||||
public class LocalBulletinBoardClient implements SubscriptionAsyncBulletinBoardClient{
|
||||
|
||||
private final BulletinBoardServer server;
|
||||
private final ListeningScheduledExecutorService executorService;
|
||||
private final BatchDigest digest;
|
||||
private final int subsrciptionDelay;
|
||||
|
||||
/**
|
||||
* Initializes an instance of the client
|
||||
* @param server an initialized Bulletin Board Server instance which will perform the actual processing of the requests
|
||||
* @param threadNum is the number of concurrent threads to allocate for the client
|
||||
* @param subscriptionDelay is the required delay between subscription calls in milliseconds
|
||||
*/
|
||||
public LocalBulletinBoardClient(BulletinBoardServer server, int threadNum, int subscriptionDelay) {
|
||||
this.server = server;
|
||||
this.executorService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(threadNum));
|
||||
this.digest = new GenericBatchDigest(new SHA256Digest());
|
||||
this.subsrciptionDelay = subscriptionDelay;
|
||||
}
|
||||
|
||||
private class MessagePoster implements Callable<Boolean> {
|
||||
|
||||
private final BulletinBoardMessage msg;
|
||||
|
||||
public MessagePoster(BulletinBoardMessage msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
return server.postMessage(msg).getValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageID postMessage(BulletinBoardMessage msg, FutureCallback<Boolean> callback) {
|
||||
|
||||
Futures.addCallback(executorService.submit(new MessagePoster(msg)), callback);
|
||||
|
||||
digest.update(msg.getMsg());
|
||||
return digest.digestAsMessageID();
|
||||
|
||||
}
|
||||
|
||||
private class CompleteBatchPoster implements Callable<Boolean> {
|
||||
|
||||
private final CompleteBatch completeBatch;
|
||||
|
||||
public CompleteBatchPoster(CompleteBatch completeBatch) {
|
||||
this.completeBatch = completeBatch;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
|
||||
if (!server.beginBatch(completeBatch.getBeginBatchMessage()).getValue())
|
||||
return false;
|
||||
|
||||
int i=0;
|
||||
for (BatchData data : completeBatch.getBatchDataList()){
|
||||
|
||||
BatchMessage message = BatchMessage.newBuilder()
|
||||
.setSignerId(completeBatch.getSignature().getSignerId())
|
||||
.setBatchId(completeBatch.getBeginBatchMessage().getBatchId())
|
||||
.setSerialNum(i)
|
||||
.setData(data)
|
||||
.build();
|
||||
|
||||
if (!server.postBatchMessage(message).getValue())
|
||||
return false;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return server.closeBatchMessage(completeBatch.getCloseBatchMessage()).getValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageID postBatch(CompleteBatch completeBatch, FutureCallback<Boolean> callback) {
|
||||
|
||||
Futures.addCallback(executorService.schedule(new CompleteBatchPoster(completeBatch), subsrciptionDelay, TimeUnit.MILLISECONDS), callback);
|
||||
|
||||
digest.update(completeBatch);
|
||||
return digest.digestAsMessageID();
|
||||
|
||||
}
|
||||
|
||||
private class BatchBeginner implements Callable<Boolean> {
|
||||
|
||||
private final BeginBatchMessage msg;
|
||||
|
||||
public BatchBeginner(BeginBatchMessage msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
return server.beginBatch(msg).getValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback<Boolean> callback) {
|
||||
Futures.addCallback(executorService.submit(new BatchBeginner(beginBatchMessage)), callback);
|
||||
}
|
||||
|
||||
private class BatchDataPoster implements Callable<Boolean> {
|
||||
|
||||
private final ByteString signerId;
|
||||
private final int batchId;
|
||||
private final List<BatchData> batchDataList;
|
||||
private final int startPosition;
|
||||
|
||||
public BatchDataPoster(ByteString signerId, int batchId, List<BatchData> batchDataList, int startPosition) {
|
||||
this.signerId = signerId;
|
||||
this.batchId = batchId;
|
||||
this.batchDataList = batchDataList;
|
||||
this.startPosition = startPosition;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
|
||||
BatchMessage.Builder msgBuilder = BatchMessage.newBuilder()
|
||||
.setSignerId(signerId)
|
||||
.setBatchId(batchId);
|
||||
|
||||
int i = startPosition;
|
||||
for (BatchData data : batchDataList){
|
||||
|
||||
msgBuilder.setSerialNum(i)
|
||||
.setData(data);
|
||||
|
||||
if (!server.postBatchMessage(msgBuilder.build()).getValue())
|
||||
return false;
|
||||
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, int startPosition, FutureCallback<Boolean> callback) {
|
||||
postBatchData(ByteString.copyFrom(signerId), batchId, batchDataList, startPosition, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
||||
postBatchData(signerId, batchId, batchDataList, 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 final CloseBatchMessage msg;
|
||||
|
||||
public BatchCloser(CloseBatchMessage msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
return server.closeBatchMessage(msg).getValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback<Boolean> callback) {
|
||||
Futures.addCallback(executorService.submit(new BatchCloser(closeBatchMessage)), callback);
|
||||
}
|
||||
|
||||
private class RedundancyGetter implements Callable<Float> {
|
||||
|
||||
private final MessageID msgId;
|
||||
|
||||
public RedundancyGetter(MessageID msgId) {
|
||||
this.msgId = msgId;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Float call() throws Exception {
|
||||
|
||||
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||
.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.MSG_ID)
|
||||
.setId(msgId.getID())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
|
||||
MessageOutputStream<BulletinBoardMessage> outputStream = new MessageOutputStream<>(byteOutputStream);
|
||||
server.readMessages(filterList,outputStream);
|
||||
|
||||
MessageInputStream<BulletinBoardMessage> inputStream =
|
||||
MessageInputStreamFactory.createMessageInputStream(
|
||||
new ByteArrayInputStream(byteOutputStream.toByteArray()),
|
||||
BulletinBoardMessage.class);
|
||||
|
||||
if (inputStream.isAvailable())
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getRedundancy(MessageID id, FutureCallback<Float> callback) {
|
||||
Futures.addCallback(executorService.submit(new RedundancyGetter(id)), callback);
|
||||
}
|
||||
|
||||
private class MessageReader implements Callable<List<BulletinBoardMessage>> {
|
||||
|
||||
private final MessageFilterList filterList;
|
||||
|
||||
public MessageReader(MessageFilterList filterList) {
|
||||
this.filterList = filterList;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<BulletinBoardMessage> call() throws Exception {
|
||||
|
||||
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
|
||||
MessageOutputStream<BulletinBoardMessage> outputStream = new MessageOutputStream<>(byteOutputStream);
|
||||
server.readMessages(filterList, outputStream);
|
||||
|
||||
MessageInputStream<BulletinBoardMessage> inputStream =
|
||||
MessageInputStreamFactory.createMessageInputStream(
|
||||
new ByteArrayInputStream(byteOutputStream.toByteArray()),
|
||||
BulletinBoardMessage.class);
|
||||
|
||||
return inputStream.asList();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
Futures.addCallback(executorService.submit(new MessageReader(filterList)), callback);
|
||||
}
|
||||
|
||||
class SubscriptionCallback implements FutureCallback<List<BulletinBoardMessage>> {
|
||||
|
||||
private MessageFilterList filterList;
|
||||
private final FutureCallback<List<BulletinBoardMessage>> callback;
|
||||
|
||||
public SubscriptionCallback(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
this.filterList = filterList;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(List<BulletinBoardMessage> result) {
|
||||
|
||||
// Report new messages to user
|
||||
callback.onSuccess(result);
|
||||
|
||||
MessageFilterList.Builder filterBuilder = filterList.toBuilder();
|
||||
|
||||
// If any new messages arrived: update the MIN_ENTRY condition
|
||||
if (result.size() > 0) {
|
||||
|
||||
// Remove last filter from list (MIN_ENTRY one)
|
||||
filterBuilder.removeFilter(filterBuilder.getFilterCount() - 1);
|
||||
|
||||
// Add updated MIN_ENTRY filter (entry number is successor of last received entry's number)
|
||||
filterBuilder.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.MIN_ENTRY)
|
||||
.setEntry(result.get(result.size() - 1).getEntryNum() + 1)
|
||||
.build());
|
||||
|
||||
}
|
||||
|
||||
filterList = filterBuilder.build();
|
||||
|
||||
// Reschedule job
|
||||
Futures.addCallback(executorService.submit(new MessageReader(filterList)), this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
|
||||
// Notify caller about failure and terminate subscription
|
||||
callback.onFailure(t);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(MessageFilterList filterList, long startEntry, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
|
||||
MessageFilterList subscriptionFilterList =
|
||||
filterList.toBuilder()
|
||||
.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.MIN_ENTRY)
|
||||
.setEntry(startEntry)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
Futures.addCallback(executorService.submit(new MessageReader(subscriptionFilterList)), new SubscriptionCallback(subscriptionFilterList, callback));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
subscribe(filterList, 0, callback);
|
||||
}
|
||||
|
||||
private class CompleteBatchReader implements Callable<CompleteBatch> {
|
||||
|
||||
private final BatchSpecificationMessage batchSpecificationMessage;
|
||||
|
||||
public CompleteBatchReader(BatchSpecificationMessage batchSpecificationMessage) {
|
||||
this.batchSpecificationMessage = batchSpecificationMessage;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CompleteBatch call() throws Exception {
|
||||
|
||||
final String[] TAGS_TO_REMOVE = {BulletinBoardConstants.BATCH_TAG, BulletinBoardConstants.BATCH_ID_TAG_PREFIX};
|
||||
|
||||
CompleteBatch completeBatch = new CompleteBatch(BeginBatchMessage.newBuilder()
|
||||
.setSignerId(batchSpecificationMessage.getSignerId())
|
||||
.setBatchId(batchSpecificationMessage.getBatchId())
|
||||
.build());
|
||||
|
||||
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
|
||||
MessageOutputStream<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()
|
||||
.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.TAG)
|
||||
.setTag(BulletinBoardConstants.BATCH_TAG)
|
||||
.build())
|
||||
.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.TAG)
|
||||
.setTag(BulletinBoardConstants.BATCH_ID_TAG_PREFIX + completeBatch.getBeginBatchMessage().getBatchId())
|
||||
.build())
|
||||
.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.SIGNER_ID)
|
||||
.setId(completeBatch.getBeginBatchMessage().getSignerId())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
byteOutputStream = new ByteArrayOutputStream();
|
||||
MessageOutputStream<BulletinBoardMessage> messageOutputStream = new MessageOutputStream<>(byteOutputStream);
|
||||
server.readMessages(filterList,messageOutputStream);
|
||||
|
||||
MessageInputStream<BulletinBoardMessage> messageInputStream =
|
||||
MessageInputStreamFactory.createMessageInputStream(
|
||||
new ByteArrayInputStream(byteOutputStream.toByteArray()),
|
||||
BulletinBoardMessage.class);
|
||||
|
||||
if (!messageInputStream.isAvailable())
|
||||
throw new NotFoundException("Batch does not exist");
|
||||
|
||||
BulletinBoardMessage message = messageInputStream.readMessage();
|
||||
|
||||
completeBatch.setBeginBatchMessage(BeginBatchMessage.newBuilder()
|
||||
.addAllTag(BulletinBoardUtils.removePrefixTags(message, Arrays.asList(TAGS_TO_REMOVE)))
|
||||
.setSignerId(message.getSig(0).getSignerId())
|
||||
.setBatchId(Integer.parseInt(BulletinBoardUtils.findTagWithPrefix(message, BulletinBoardConstants.BATCH_ID_TAG_PREFIX)))
|
||||
.build());
|
||||
|
||||
completeBatch.setSignature(message.getSig(0));
|
||||
completeBatch.setTimestamp(message.getMsg().getTimestamp());
|
||||
|
||||
return completeBatch;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback<CompleteBatch> callback) {
|
||||
Futures.addCallback(executorService.submit(new CompleteBatchReader(batchSpecificationMessage)), callback);
|
||||
}
|
||||
|
||||
private class SyncQueryHandler implements Callable<SyncQueryResponse> {
|
||||
|
||||
private final SyncQuery syncQuery;
|
||||
|
||||
public SyncQueryHandler(SyncQuery syncQuery) {
|
||||
this.syncQuery = syncQuery;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SyncQueryResponse call() throws Exception {
|
||||
return server.querySync(syncQuery);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void querySync(SyncQuery syncQuery, FutureCallback<SyncQueryResponse> callback) {
|
||||
Futures.addCallback(executorService.submit(new SyncQueryHandler(syncQuery)), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is a stub, since the implementation only considers one server, and that is given in the constructor
|
||||
* @param ignored is ignored
|
||||
*/
|
||||
@Override
|
||||
public void init(BulletinBoardClientParams ignored) {}
|
||||
|
||||
@Override
|
||||
public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||
|
||||
try {
|
||||
|
||||
MessagePoster poster = new MessagePoster(msg);
|
||||
poster.call();
|
||||
|
||||
digest.update(msg);
|
||||
return digest.digestAsMessageID();
|
||||
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getRedundancy(MessageID id) {
|
||||
|
||||
try {
|
||||
|
||||
RedundancyGetter getter = new RedundancyGetter(id);
|
||||
return getter.call();
|
||||
|
||||
} catch (Exception e) {
|
||||
return -1.0f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) {
|
||||
|
||||
try {
|
||||
|
||||
MessageReader reader = new MessageReader(filterList);
|
||||
return reader.call();
|
||||
|
||||
} catch (Exception e){
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public SyncQuery generateSyncQuery(GenerateSyncQueryParams GenerateSyncQueryParams) throws CommunicationException {
|
||||
return server.generateSyncQuery(GenerateSyncQueryParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
server.close();
|
||||
} catch (CommunicationException ignored) {}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||
*
|
||||
* This is a general class for handling multi-server work
|
||||
* It utilizes Single Server Clients to perform the actual per-server work
|
||||
*/
|
||||
public abstract class MultiServerWorker<IN, OUT> extends BulletinClientWorker<IN> implements Runnable, FutureCallback<OUT>{
|
||||
|
||||
private 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 maxFailedServers; // The maximal number of allowed server failures
|
||||
|
||||
private AtomicBoolean returnedResult;
|
||||
|
||||
private final FutureCallback<OUT> futureCallback;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param clients contains a list of Single Server clients to handle requests
|
||||
* @param shuffleClients is a boolean stating whether or not it is needed to shuffle the clients
|
||||
* @param minServers is the minimal amount of servers needed in order to successfully complete the job
|
||||
* @param payload is the payload for the job
|
||||
* @param maxRetry is the maximal per-server retry count
|
||||
* @param futureCallback contains the callback methods used to report the result back to the client
|
||||
*/
|
||||
public MultiServerWorker(List<SingleServerBulletinBoardClient> clients, boolean shuffleClients,
|
||||
int minServers, IN payload, int maxRetry,
|
||||
FutureCallback<OUT> futureCallback) {
|
||||
|
||||
super(payload,maxRetry);
|
||||
|
||||
this.clients = clients;
|
||||
if (shuffleClients){
|
||||
Collections.shuffle(clients);
|
||||
}
|
||||
|
||||
this.minServers = new AtomicInteger(minServers);
|
||||
maxFailedServers = new AtomicInteger(clients.size() - minServers);
|
||||
this.futureCallback = futureCallback;
|
||||
|
||||
returnedResult = new AtomicBoolean(false);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor overload without client shuffling
|
||||
*/
|
||||
public MultiServerWorker(List<SingleServerBulletinBoardClient> clients,
|
||||
int minServers, IN payload, int maxRetry,
|
||||
FutureCallback<OUT> futureCallback) {
|
||||
|
||||
this(clients, false, minServers, payload, maxRetry, futureCallback);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to report a successful operation to the client
|
||||
* Only reports once to the client
|
||||
* @param result is the result
|
||||
*/
|
||||
protected void succeed(OUT result){
|
||||
if (returnedResult.compareAndSet(false, true)) {
|
||||
futureCallback.onSuccess(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to report a failed operation to the client
|
||||
* Only reports once to the client
|
||||
* @param t contains the error/exception that occurred
|
||||
*/
|
||||
protected void fail(Throwable t){
|
||||
if (returnedResult.compareAndSet(false, true)) {
|
||||
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() {
|
||||
return clients.size();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,161 +1,192 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.crypto.Digest;
|
||||
import meerkat.crypto.concrete.SHA256Digest;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.protobuf.Voting;
|
||||
import meerkat.protobuf.Voting.BulletinBoardClientParams;
|
||||
import meerkat.rest.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 05-Dec-15.
|
||||
*/
|
||||
public class SimpleBulletinBoardClient{ //implements BulletinBoardClient {
|
||||
|
||||
private List<String> meerkatDBs;
|
||||
|
||||
private Client client;
|
||||
|
||||
private Digest digest;
|
||||
|
||||
/**
|
||||
* Stores database locations and initializes the web Client
|
||||
* @param clientParams contains the data needed to access the DBs
|
||||
*/
|
||||
// @Override
|
||||
public void init(Voting.BulletinBoardClientParams clientParams) {
|
||||
|
||||
meerkatDBs = clientParams.getBulletinBoardAddressList();
|
||||
|
||||
client = ClientBuilder.newClient();
|
||||
client.register(ProtobufMessageBodyReader.class);
|
||||
client.register(ProtobufMessageBodyWriter.class);
|
||||
|
||||
digest = new SHA256Digest();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Post message to all DBs
|
||||
* Make only one try per DB.
|
||||
* @param msg is the message,
|
||||
* @return the message ID for later retrieval
|
||||
* @throws CommunicationException
|
||||
*/
|
||||
// @Override
|
||||
public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||
|
||||
WebTarget webTarget;
|
||||
Response response;
|
||||
|
||||
// Post message to all databases
|
||||
try {
|
||||
for (String db : meerkatDBs) {
|
||||
webTarget = client.target(db).path(Constants.BULLETIN_BOARD_SERVER_PATH).path(Constants.POST_MESSAGE_PATH);
|
||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
||||
|
||||
// Only consider valid responses
|
||||
if (response.getStatusInfo() == Response.Status.OK
|
||||
|| response.getStatusInfo() == Response.Status.CREATED) {
|
||||
response.readEntity(BoolMsg.class).getValue();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) { // Occurs only when server replies with valid status but invalid data
|
||||
throw new CommunicationException("Error accessing database: " + e.getMessage());
|
||||
}
|
||||
|
||||
// Calculate the correct message ID and return it
|
||||
digest.reset();
|
||||
digest.update(msg.getMsg());
|
||||
return MessageID.newBuilder().setID(ByteString.copyFrom(digest.digest())).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Access each database and search for a given message ID
|
||||
* Return the number of databases in which the message was found
|
||||
* Only try once per DB
|
||||
* Ignore communication exceptions in specific databases
|
||||
* @param id is the requested message ID
|
||||
* @return the number of DBs in which retrieval was successful
|
||||
*/
|
||||
// @Override
|
||||
public float getRedundancy(MessageID id) {
|
||||
WebTarget webTarget;
|
||||
Response response;
|
||||
|
||||
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||
.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.MSG_ID)
|
||||
.setId(id.getID())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
float count = 0;
|
||||
|
||||
for (String db : meerkatDBs) {
|
||||
try {
|
||||
webTarget = client.target(db).path(Constants.BULLETIN_BOARD_SERVER_PATH).path(Constants.READ_MESSAGES_PATH);
|
||||
|
||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF));
|
||||
|
||||
if (response.readEntity(BulletinBoardMessageList.class).getMessageCount() > 0){
|
||||
count++;
|
||||
}
|
||||
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
return count / ((float) meerkatDBs.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through the DBs and try to retrieve messages according to the specified filter
|
||||
* If at the operation is successful for some DB: return the results and stop iterating
|
||||
* If no operation is successful: return null (NOT blank list)
|
||||
* @param filterList return only messages that match the filters (null means no filtering).
|
||||
* @return
|
||||
*/
|
||||
// @Override
|
||||
public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) {
|
||||
WebTarget webTarget;
|
||||
Response response;
|
||||
BulletinBoardMessageList messageList;
|
||||
|
||||
// Replace null filter list with blank one.
|
||||
if (filterList == null){
|
||||
filterList = MessageFilterList.newBuilder().build();
|
||||
}
|
||||
|
||||
for (String db : meerkatDBs) {
|
||||
try {
|
||||
webTarget = client.target(db).path(Constants.BULLETIN_BOARD_SERVER_PATH).path(Constants.READ_MESSAGES_PATH);
|
||||
|
||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF));
|
||||
|
||||
messageList = response.readEntity(BulletinBoardMessageList.class);
|
||||
|
||||
if (messageList != null){
|
||||
return messageList.getMessageList();
|
||||
}
|
||||
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void registerNewMessageCallback(MessageCallback callback, MessageFilterList filterList) {
|
||||
// callback.handleNewMessage(readMessages(filterList));
|
||||
// }
|
||||
}
|
||||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.protobuf.BoolValue;
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.crypto.Digest;
|
||||
import meerkat.crypto.concrete.SHA256Digest;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.protobuf.Comm.*;
|
||||
import meerkat.protobuf.Voting.*;
|
||||
import meerkat.rest.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.*;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 05-Dec-15.
|
||||
* Implements BulletinBoardClient interface in a simple, straightforward manner
|
||||
*/
|
||||
public class SimpleBulletinBoardClient implements BulletinBoardClient{
|
||||
|
||||
protected List<String> meerkatDBs;
|
||||
|
||||
protected Client client;
|
||||
|
||||
protected Digest digest;
|
||||
|
||||
/**
|
||||
* Stores database locations and initializes the web Client
|
||||
* @param clientParams contains the data needed to access the DBs
|
||||
*/
|
||||
@Override
|
||||
public void init(BulletinBoardClientParams clientParams) {
|
||||
|
||||
this.meerkatDBs = clientParams.getBulletinBoardAddressList();
|
||||
|
||||
client = ClientBuilder.newClient();
|
||||
client.register(ProtobufMessageBodyReader.class);
|
||||
client.register(ProtobufMessageBodyWriter.class);
|
||||
|
||||
digest = new SHA256Digest();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Post message to all DBs
|
||||
* Make only one try per DB.
|
||||
* @param msg is the message,
|
||||
* @return the message ID for later retrieval
|
||||
* @throws CommunicationException
|
||||
*/
|
||||
@Override
|
||||
public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||
|
||||
WebTarget webTarget;
|
||||
Response response;
|
||||
|
||||
// Post message to all databases
|
||||
try {
|
||||
for (String db : meerkatDBs) {
|
||||
webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(POST_MESSAGE_PATH);
|
||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
||||
|
||||
// Only consider valid responses
|
||||
if (response.getStatusInfo() == Response.Status.OK
|
||||
|| response.getStatusInfo() == Response.Status.CREATED) {
|
||||
response.readEntity(BoolValue.class).getValue();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) { // Occurs only when server replies with valid status but invalid data
|
||||
throw new CommunicationException("Error accessing database: " + e.getMessage());
|
||||
}
|
||||
|
||||
// Calculate the correct message ID and return it
|
||||
digest.reset();
|
||||
digest.update(msg.getMsg());
|
||||
return MessageID.newBuilder().setID(ByteString.copyFrom(digest.digest())).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Access each database and search for a given message ID
|
||||
* Return the number of databases in which the message was found
|
||||
* Only try once per DB
|
||||
* Ignore communication exceptions in specific databases
|
||||
* @param id is the requested message ID
|
||||
* @return the number of DBs in which retrieval was successful
|
||||
*/
|
||||
@Override
|
||||
public float getRedundancy(MessageID id) {
|
||||
WebTarget webTarget;
|
||||
Response response;
|
||||
|
||||
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||
.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.MSG_ID)
|
||||
.setId(id.getID())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
float count = 0;
|
||||
|
||||
for (String db : meerkatDBs) {
|
||||
try {
|
||||
webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH);
|
||||
|
||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF));
|
||||
|
||||
if (response.readEntity(BulletinBoardMessageList.class).getMessageCount() > 0){
|
||||
count++;
|
||||
}
|
||||
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
return count / ((float) meerkatDBs.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through the DBs and try to retrieve messages according to the specified filter
|
||||
* If at the operation is successful for some DB: return the results and stop iterating
|
||||
* If no operation is successful: return null (NOT blank list)
|
||||
* @param filterList return only messages that match the filters (null means no filtering).
|
||||
* @return the list of Bulletin Board messages that are returned from a server
|
||||
*/
|
||||
@Override
|
||||
public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) {
|
||||
|
||||
WebTarget webTarget;
|
||||
Response response;
|
||||
BulletinBoardMessageList messageList;
|
||||
|
||||
// Replace null filter list with blank one.
|
||||
if (filterList == null){
|
||||
filterList = MessageFilterList.newBuilder().build();
|
||||
}
|
||||
|
||||
for (String db : meerkatDBs) {
|
||||
|
||||
try {
|
||||
webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH);
|
||||
|
||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF));
|
||||
|
||||
messageList = response.readEntity(BulletinBoardMessageList.class);
|
||||
|
||||
if (messageList != null){
|
||||
return messageList.getMessageList();
|
||||
}
|
||||
|
||||
} catch (Exception e) {}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException {
|
||||
|
||||
WebTarget webTarget;
|
||||
Response response;
|
||||
|
||||
for (String db : meerkatDBs) {
|
||||
|
||||
try {
|
||||
webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(GENERATE_SYNC_QUERY_PATH);
|
||||
|
||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(generateSyncQueryParams, Constants.MEDIATYPE_PROTOBUF));
|
||||
|
||||
return response.readEntity(SyncQuery.class);
|
||||
|
||||
} catch (Exception e) {}
|
||||
|
||||
}
|
||||
|
||||
throw new CommunicationException("Could not contact any server");
|
||||
|
||||
}
|
||||
|
||||
public void close() {
|
||||
client.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,626 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.bulletinboard.workers.singleserver.*;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.protobuf.Voting.BulletinBoardClientParams;
|
||||
import meerkat.util.BulletinBoardUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 28-Dec-15.
|
||||
*
|
||||
* This class implements the asynchronous Bulletin Board Client interface
|
||||
* It only handles a single Bulletin Board Server
|
||||
* If the list of servers contains more than one server: the server actually used is the first one
|
||||
* The class further implements a delayed access to the server after a communication error occurs
|
||||
*/
|
||||
public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient implements SubscriptionAsyncBulletinBoardClient {
|
||||
|
||||
private final int MAX_RETRIES = 11;
|
||||
|
||||
private ListeningScheduledExecutorService executorService;
|
||||
|
||||
protected BatchDigest batchDigest;
|
||||
|
||||
private long lastServerErrorTime;
|
||||
|
||||
private final long failDelayInMilliseconds;
|
||||
|
||||
private final long subscriptionIntervalInMilliseconds;
|
||||
|
||||
/**
|
||||
* Notify the client that a job has failed
|
||||
* This makes new scheduled jobs be scheduled for a later time (after the given delay)
|
||||
*/
|
||||
protected void fail() {
|
||||
|
||||
// Update last fail time
|
||||
lastServerErrorTime = System.currentTimeMillis();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 not accessible: the job is scheduled for a later time
|
||||
* @param worker is the worker that should be scheduled for work
|
||||
* @param callback is the class containing callbacks for handling job completion/failure
|
||||
*/
|
||||
protected void scheduleWorker(SingleServerWorker worker, FutureCallback callback){
|
||||
|
||||
long timeSinceLastServerError = System.currentTimeMillis() - lastServerErrorTime;
|
||||
|
||||
if (timeSinceLastServerError >= failDelayInMilliseconds) {
|
||||
|
||||
// Schedule for immediate processing
|
||||
Futures.addCallback(executorService.submit(worker), callback);
|
||||
|
||||
} else {
|
||||
|
||||
// Schedule for processing immediately following delay expiry
|
||||
Futures.addCallback(executorService.schedule(
|
||||
worker,
|
||||
failDelayInMilliseconds - timeSinceLastServerError,
|
||||
TimeUnit.MILLISECONDS),
|
||||
callback);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class for handling simple operation results and retrying if needed
|
||||
*/
|
||||
class RetryCallback<T> implements FutureCallback<T> {
|
||||
|
||||
private final SingleServerWorker worker;
|
||||
private final FutureCallback<T> futureCallback;
|
||||
|
||||
public RetryCallback(SingleServerWorker worker, FutureCallback<T> futureCallback) {
|
||||
this.worker = worker;
|
||||
this.futureCallback = futureCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(T result) {
|
||||
futureCallback.onSuccess(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
|
||||
// Notify client about failure
|
||||
fail();
|
||||
|
||||
// Check if another attempt should be made
|
||||
|
||||
worker.decMaxRetry();
|
||||
|
||||
if (worker.isRetry()) {
|
||||
// Perform another attempt
|
||||
scheduleWorker(worker, this);
|
||||
} else {
|
||||
// No more retries: notify caller about failure
|
||||
futureCallback.onFailure(t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This callback ties together all the per-batch-data callbacks into a single callback
|
||||
* It reports success back to the user only if all of the batch-data were successfully posted
|
||||
* If any batch-data fails to post: this callback reports failure
|
||||
*/
|
||||
class PostBatchDataListCallback implements FutureCallback<Boolean> {
|
||||
|
||||
private final FutureCallback<Boolean> callback;
|
||||
|
||||
private AtomicInteger batchDataRemaining;
|
||||
private AtomicBoolean aggregatedResult;
|
||||
|
||||
public PostBatchDataListCallback(int batchDataLength, FutureCallback<Boolean> callback) {
|
||||
|
||||
this.callback = callback;
|
||||
this.batchDataRemaining = new AtomicInteger(batchDataLength);
|
||||
this.aggregatedResult = new AtomicBoolean(false);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
|
||||
if (result){
|
||||
this.aggregatedResult.set(true);
|
||||
}
|
||||
|
||||
if (batchDataRemaining.decrementAndGet() == 0){
|
||||
callback.onSuccess(this.aggregatedResult.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
|
||||
// Notify caller about failure
|
||||
callback.onFailure(t);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This callback ties together the different parts of a CompleteBatch as they arrive from the server
|
||||
* It assembles a CompleteBatch from the parts and sends it to the user if all parts arrived
|
||||
* If any part fails to arrive: it invokes the onFailure method
|
||||
*/
|
||||
class CompleteBatchReadCallback {
|
||||
|
||||
private final FutureCallback<CompleteBatch> callback;
|
||||
|
||||
private List<BatchData> batchDataList;
|
||||
private BulletinBoardMessage batchMessage;
|
||||
|
||||
private AtomicInteger remainingQueries;
|
||||
private AtomicBoolean failed;
|
||||
|
||||
public CompleteBatchReadCallback(FutureCallback<CompleteBatch> callback) {
|
||||
|
||||
this.callback = callback;
|
||||
|
||||
remainingQueries = new AtomicInteger(2);
|
||||
failed = new AtomicBoolean(false);
|
||||
|
||||
}
|
||||
|
||||
protected void combineAndReturn() {
|
||||
|
||||
final String[] prefixes = {
|
||||
BulletinBoardConstants.BATCH_ID_TAG_PREFIX,
|
||||
BulletinBoardConstants.BATCH_TAG};
|
||||
|
||||
if (remainingQueries.decrementAndGet() == 0){
|
||||
|
||||
String batchIdStr = BulletinBoardUtils.findTagWithPrefix(batchMessage, BulletinBoardConstants.BATCH_ID_TAG_PREFIX);
|
||||
|
||||
if (batchIdStr == null){
|
||||
callback.onFailure(new CommunicationException("Server returned invalid message with no Batch ID tag"));
|
||||
}
|
||||
|
||||
BeginBatchMessage beginBatchMessage =
|
||||
BeginBatchMessage.newBuilder()
|
||||
.setSignerId(batchMessage.getSig(0).getSignerId())
|
||||
.setBatchId(Integer.parseInt(batchIdStr))
|
||||
.addAllTag(BulletinBoardUtils.removePrefixTags(batchMessage, Arrays.asList(prefixes)))
|
||||
.build();
|
||||
callback.onSuccess(new CompleteBatch(beginBatchMessage, batchDataList, batchMessage.getSig(0)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void fail(Throwable t) {
|
||||
if (failed.compareAndSet(false, true)) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a FutureCallback for the Batch Data List that ties to this object
|
||||
*/
|
||||
public FutureCallback<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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class for handling returned values of subscription operations
|
||||
* This class's methods also ensure continued operation of the subscription
|
||||
*/
|
||||
class SubscriptionCallback implements FutureCallback<List<BulletinBoardMessage>> {
|
||||
|
||||
private SingleServerReadMessagesWorker worker;
|
||||
private final FutureCallback<List<BulletinBoardMessage>> callback;
|
||||
|
||||
private MessageFilterList.Builder filterBuilder;
|
||||
|
||||
public SubscriptionCallback(SingleServerReadMessagesWorker worker, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
this.worker = worker;
|
||||
this.callback = callback;
|
||||
filterBuilder = worker.getPayload().toBuilder();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(List<BulletinBoardMessage> result) {
|
||||
|
||||
// Report new messages to user
|
||||
callback.onSuccess(result);
|
||||
|
||||
// Remove last filter from list (MIN_ENTRY one)
|
||||
filterBuilder.removeFilter(filterBuilder.getFilterCount() - 1);
|
||||
|
||||
// Add updated MIN_ENTRY filter (entry number is successor of last received entry's number)
|
||||
filterBuilder.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.MIN_ENTRY)
|
||||
.setEntry(result.get(result.size() - 1).getEntryNum() + 1)
|
||||
.build());
|
||||
|
||||
// Create new worker with updated task
|
||||
worker = new SingleServerReadMessagesWorker(worker.serverAddress, filterBuilder.build(), 1);
|
||||
|
||||
// Schedule the worker
|
||||
scheduleWorker(worker, this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
|
||||
// Notify client about failure
|
||||
fail();
|
||||
|
||||
// Notify caller about failure and terminate subscription
|
||||
callback.onFailure(t);
|
||||
}
|
||||
}
|
||||
|
||||
public SingleServerBulletinBoardClient(ListeningScheduledExecutorService executorService,
|
||||
long failDelayInMilliseconds,
|
||||
long subscriptionIntervalInMilliseconds) {
|
||||
|
||||
this.executorService = executorService;
|
||||
|
||||
this.failDelayInMilliseconds = failDelayInMilliseconds;
|
||||
this.subscriptionIntervalInMilliseconds = subscriptionIntervalInMilliseconds;
|
||||
|
||||
// Set server error time to a time sufficiently in the past to make new jobs go through
|
||||
lastServerErrorTime = System.currentTimeMillis() - failDelayInMilliseconds;
|
||||
|
||||
}
|
||||
|
||||
public SingleServerBulletinBoardClient(int threadPoolSize, long failDelayInMilliseconds, long subscriptionIntervalInMilliseconds) {
|
||||
|
||||
this(MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(threadPoolSize)),
|
||||
failDelayInMilliseconds,
|
||||
subscriptionIntervalInMilliseconds);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores database location, initializes the web Client and
|
||||
* @param clientParams contains the data needed to access the DBs
|
||||
*/
|
||||
@Override
|
||||
public void init(BulletinBoardClientParams clientParams) {
|
||||
|
||||
// Perform usual setup
|
||||
super.init(clientParams);
|
||||
|
||||
// Wrap the Digest into a BatchDigest
|
||||
batchDigest = new GenericBatchDigest(digest);
|
||||
|
||||
// Remove all but first DB address
|
||||
String dbAddress = meerkatDBs.get(0);
|
||||
meerkatDBs = new LinkedList<>();
|
||||
meerkatDBs.add(dbAddress);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageID postMessage(BulletinBoardMessage msg, FutureCallback<Boolean> callback) {
|
||||
|
||||
// Create worker with redundancy 1 and MAX_RETRIES retries
|
||||
SingleServerPostMessageWorker worker = new SingleServerPostMessageWorker(meerkatDBs.get(0), msg, MAX_RETRIES);
|
||||
|
||||
// Submit worker and create callback
|
||||
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
||||
|
||||
// Calculate the correct message ID and return it
|
||||
batchDigest.reset();
|
||||
batchDigest.update(msg.getMsg());
|
||||
return batchDigest.digestAsMessageID();
|
||||
|
||||
}
|
||||
|
||||
private class PostBatchDataCallback implements FutureCallback<Boolean> {
|
||||
|
||||
private final CompleteBatch completeBatch;
|
||||
private final FutureCallback<Boolean> callback;
|
||||
|
||||
public PostBatchDataCallback(CompleteBatch completeBatch, FutureCallback<Boolean> callback) {
|
||||
this.completeBatch = completeBatch;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Boolean msg) {
|
||||
closeBatch(
|
||||
completeBatch.getCloseBatchMessage(),
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class BeginBatchCallback implements FutureCallback<Boolean> {
|
||||
|
||||
private final CompleteBatch completeBatch;
|
||||
private final FutureCallback<Boolean> callback;
|
||||
|
||||
public BeginBatchCallback(CompleteBatch completeBatch, FutureCallback<Boolean> callback) {
|
||||
this.completeBatch = completeBatch;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Boolean msg) {
|
||||
|
||||
postBatchData(
|
||||
completeBatch.getBeginBatchMessage().getSignerId(),
|
||||
completeBatch.getBeginBatchMessage().getBatchId(),
|
||||
completeBatch.getBatchDataList(),
|
||||
0,
|
||||
new PostBatchDataCallback(completeBatch,callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageID postBatch(CompleteBatch completeBatch, FutureCallback<Boolean> callback) {
|
||||
|
||||
beginBatch(
|
||||
completeBatch.getBeginBatchMessage(),
|
||||
new BeginBatchCallback(completeBatch, callback)
|
||||
);
|
||||
|
||||
batchDigest.update(completeBatch);
|
||||
|
||||
return batchDigest.digestAsMessageID();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback<Boolean> callback) {
|
||||
|
||||
// Create worker with redundancy 1 and MAX_RETRIES retries
|
||||
SingleServerBeginBatchWorker worker =
|
||||
new SingleServerBeginBatchWorker(meerkatDBs.get(0), beginBatchMessage, MAX_RETRIES);
|
||||
|
||||
// Submit worker and create callback
|
||||
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList,
|
||||
int startPosition, FutureCallback<Boolean> callback) {
|
||||
|
||||
BatchMessage.Builder builder = BatchMessage.newBuilder()
|
||||
.setSignerId(signerId)
|
||||
.setBatchId(batchId);
|
||||
|
||||
// Create a unified callback to aggregate successful posts
|
||||
|
||||
PostBatchDataListCallback listCallback = new PostBatchDataListCallback(batchDataList.size(), callback);
|
||||
|
||||
// Iterate through data list
|
||||
|
||||
for (BatchData data : batchDataList) {
|
||||
builder.setSerialNum(startPosition).setData(data);
|
||||
|
||||
// Create worker with redundancy 1 and MAX_RETRIES retries
|
||||
SingleServerPostBatchWorker worker =
|
||||
new SingleServerPostBatchWorker(meerkatDBs.get(0), builder.build(), MAX_RETRIES);
|
||||
|
||||
// Create worker with redundancy 1 and MAX_RETRIES retries
|
||||
scheduleWorker(worker, new RetryCallback<>(worker, listCallback));
|
||||
|
||||
// Increment position in batch
|
||||
startPosition++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
||||
|
||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList,
|
||||
int startPosition, FutureCallback<Boolean> callback) {
|
||||
|
||||
postBatchData(ByteString.copyFrom(signerId), batchId, batchDataList, startPosition, callback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
||||
|
||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback<Boolean> callback) {
|
||||
|
||||
// Create worker with redundancy 1 and MAX_RETRIES retries
|
||||
SingleServerCloseBatchWorker worker =
|
||||
new SingleServerCloseBatchWorker(meerkatDBs.get(0), closeBatchMessage, MAX_RETRIES);
|
||||
|
||||
// Submit worker and create callback
|
||||
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getRedundancy(MessageID id, FutureCallback<Float> callback) {
|
||||
|
||||
// Create worker with no retries
|
||||
SingleServerGetRedundancyWorker worker = new SingleServerGetRedundancyWorker(meerkatDBs.get(0), id, 1);
|
||||
|
||||
// Submit job and create callback
|
||||
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
|
||||
// Create job with no retries
|
||||
SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(meerkatDBs.get(0), filterList, 1);
|
||||
|
||||
// Submit job and create callback
|
||||
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback<CompleteBatch> callback) {
|
||||
|
||||
// Create job with no retries for retrieval of the Bulletin Board Message that defines the batch
|
||||
|
||||
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||
.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.TAG)
|
||||
.setTag(BulletinBoardConstants.BATCH_TAG)
|
||||
.build())
|
||||
.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.TAG)
|
||||
.setTag(BulletinBoardConstants.BATCH_ID_TAG_PREFIX + batchSpecificationMessage.getBatchId())
|
||||
.build())
|
||||
.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.SIGNER_ID)
|
||||
.setId(batchSpecificationMessage.getSignerId())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
SingleServerReadMessagesWorker messageWorker = new SingleServerReadMessagesWorker(meerkatDBs.get(0), filterList, 1);
|
||||
|
||||
// Create job with no retries for retrieval of the Batch Data List
|
||||
SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(meerkatDBs.get(0), batchSpecificationMessage, 1);
|
||||
|
||||
// Create callback that will combine the two worker products
|
||||
CompleteBatchReadCallback completeBatchReadCallback = new CompleteBatchReadCallback(callback);
|
||||
|
||||
// Submit jobs with wrapped callbacks
|
||||
scheduleWorker(messageWorker, new RetryCallback<>(messageWorker, completeBatchReadCallback.asBulletinBoardMessageListFutureCallback()));
|
||||
scheduleWorker(batchWorker, new RetryCallback<>(batchWorker, completeBatchReadCallback.asBatchDataListFutureCallback()));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void querySync(SyncQuery syncQuery, FutureCallback<SyncQueryResponse> callback) {
|
||||
|
||||
SingleServerQuerySyncWorker worker = new SingleServerQuerySyncWorker(meerkatDBs.get(0), syncQuery, MAX_RETRIES);
|
||||
|
||||
scheduleWorker(worker, new RetryCallback<>(worker, callback));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(MessageFilterList filterList, long startEntry, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
// Remove all existing MIN_ENTRY filters and create new one that starts at 0
|
||||
|
||||
MessageFilterList.Builder filterListBuilder = filterList.toBuilder();
|
||||
|
||||
Iterator<MessageFilter> iterator = filterListBuilder.getFilterList().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
MessageFilter filter = iterator.next();
|
||||
|
||||
if (filter.getType() == FilterType.MIN_ENTRY){
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
filterListBuilder.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.MIN_ENTRY)
|
||||
.setEntry(startEntry)
|
||||
.build());
|
||||
|
||||
// Create job with no retries
|
||||
SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(meerkatDBs.get(0), filterListBuilder.build(), MAX_RETRIES);
|
||||
|
||||
// Submit job and create callback that retries on failure and handles repeated subscription
|
||||
scheduleWorker(worker, new RetryCallback<>(worker, new SubscriptionCallback(worker, callback)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
subscribe(filterList, 0, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
super.close();
|
||||
|
||||
executorService.shutdown();
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import meerkat.rest.ProtobufMessageBodyReader;
|
||||
import meerkat.rest.ProtobufMessageBodyWriter;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 02-Jan-16.
|
||||
*/
|
||||
public abstract class SingleServerWorker<IN, OUT> extends BulletinClientWorker<IN> implements Callable<OUT>{
|
||||
|
||||
// This resource enabled creation of a single Client per thread.
|
||||
protected static final ThreadLocal<Client> clientLocal =
|
||||
new ThreadLocal<Client> () {
|
||||
@Override protected Client initialValue() {
|
||||
Client client;
|
||||
client = ClientBuilder.newClient();
|
||||
client.register(ProtobufMessageBodyReader.class);
|
||||
client.register(ProtobufMessageBodyWriter.class);
|
||||
|
||||
return client;
|
||||
}
|
||||
};
|
||||
|
||||
protected final String serverAddress;
|
||||
|
||||
public SingleServerWorker(String serverAddress, IN payload, int maxRetry) {
|
||||
super(payload, maxRetry);
|
||||
this.serverAddress = serverAddress;
|
||||
}
|
||||
|
||||
public String getServerAddress() {
|
||||
return serverAddress;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,131 +1,255 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.common.util.concurrent.*;
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.bulletinboard.callbacks.GetRedundancyFutureCallback;
|
||||
import meerkat.bulletinboard.callbacks.PostMessageFutureCallback;
|
||||
import meerkat.bulletinboard.callbacks.ReadMessagesFutureCallback;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.crypto.Digest;
|
||||
import meerkat.crypto.concrete.SHA256Digest;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.protobuf.Voting;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 05-Dec-15.
|
||||
* Thread-based implementation of a Bulletin Board Client.
|
||||
* Features:
|
||||
* 1. Handles tasks concurrently.
|
||||
* 2. Retries submitting
|
||||
*/
|
||||
public class ThreadedBulletinBoardClient implements BulletinBoardClient {
|
||||
|
||||
private final static int THREAD_NUM = 10;
|
||||
ListeningExecutorService listeningExecutor;
|
||||
|
||||
private Digest digest;
|
||||
|
||||
private List<String> meerkatDBs;
|
||||
private String postSubAddress;
|
||||
private String readSubAddress;
|
||||
|
||||
private final static int READ_MESSAGES_RETRY_NUM = 1;
|
||||
|
||||
private int minAbsoluteRedundancy;
|
||||
|
||||
/**
|
||||
* Stores database locations and initializes the web Client
|
||||
* Stores the required minimum redundancy.
|
||||
* Starts the Thread Pool.
|
||||
* @param clientParams contains the required information
|
||||
*/
|
||||
@Override
|
||||
public void init(Voting.BulletinBoardClientParams clientParams) {
|
||||
|
||||
meerkatDBs = clientParams.getBulletinBoardAddressList();
|
||||
|
||||
minAbsoluteRedundancy = (int) (clientParams.getMinRedundancy() * meerkatDBs.size());
|
||||
|
||||
listeningExecutor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(THREAD_NUM));
|
||||
|
||||
digest = new SHA256Digest();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Post message to all DBs
|
||||
* Retry failed DBs
|
||||
* @param msg is the message,
|
||||
* @return the message ID for later retrieval
|
||||
* @throws CommunicationException
|
||||
*/
|
||||
@Override
|
||||
public MessageID postMessage(BulletinBoardMessage msg, ClientCallback<?> callback){
|
||||
|
||||
// Create job
|
||||
BulletinClientJob job = new BulletinClientJob(meerkatDBs, minAbsoluteRedundancy, BulletinClientJob.JobType.POST_MESSAGE, msg, -1);
|
||||
|
||||
// Submit job and create callback
|
||||
Futures.addCallback(listeningExecutor.submit(new BulletinClientWorker(job)), new PostMessageFutureCallback(listeningExecutor, callback));
|
||||
|
||||
// Calculate the correct message ID and return it
|
||||
digest.reset();
|
||||
digest.update(msg.getMsg());
|
||||
return MessageID.newBuilder().setID(ByteString.copyFrom(digest.digest())).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Access each database and search for a given message ID
|
||||
* Return the number of databases in which the message was found
|
||||
* Only try once per DB
|
||||
* Ignore communication exceptions in specific databases
|
||||
* @param id is the requested message ID
|
||||
* @return the number of DBs in which retrieval was successful
|
||||
*/
|
||||
@Override
|
||||
public void getRedundancy(MessageID id, ClientCallback<Float> callback) {
|
||||
|
||||
// Create job
|
||||
BulletinClientJob job = new BulletinClientJob(meerkatDBs, minAbsoluteRedundancy, BulletinClientJob.JobType.GET_REDUNDANCY, id, 1);
|
||||
|
||||
// Submit job and create callback
|
||||
Futures.addCallback(listeningExecutor.submit(new BulletinClientWorker(job)), new GetRedundancyFutureCallback(listeningExecutor, callback));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through the DBs and try to retrieve messages according to the specified filter
|
||||
* If at the operation is successful for some DB: return the results and stop iterating
|
||||
* If no operation is successful: return null (NOT blank list)
|
||||
* @param filterList return only messages that match the filters (null means no filtering).
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public void readMessages(MessageFilterList filterList, ClientCallback<List<BulletinBoardMessage>> callback) {
|
||||
|
||||
// Create job
|
||||
BulletinClientJob job = new BulletinClientJob(meerkatDBs, minAbsoluteRedundancy, BulletinClientJob.JobType.READ_MESSAGES,
|
||||
filterList, READ_MESSAGES_RETRY_NUM);
|
||||
|
||||
// Submit job and create callback
|
||||
Futures.addCallback(listeningExecutor.submit(new BulletinClientWorker(job)), new ReadMessagesFutureCallback(listeningExecutor, callback));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
listeningExecutor.shutdown();
|
||||
while (! listeningExecutor.isShutdown()) {
|
||||
listeningExecutor.awaitTermination(10, TimeUnit.SECONDS);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
System.err.println(e.getCause() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import meerkat.bulletinboard.workers.multiserver.*;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.protobuf.BulletinBoardAPI;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.protobuf.Voting.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 05-Dec-15.
|
||||
* Thread-based implementation of a Async Bulletin Board Client.
|
||||
* Features:
|
||||
* 1. Handles tasks concurrently.
|
||||
* 2. Retries submitting
|
||||
*/
|
||||
public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient implements AsyncBulletinBoardClient {
|
||||
|
||||
// Executor service for handling jobs
|
||||
private final static int JOBS_THREAD_NUM = 5;
|
||||
private ExecutorService executorService;
|
||||
|
||||
// Per-server clients
|
||||
private List<SingleServerBulletinBoardClient> clients;
|
||||
|
||||
private BatchDigest batchDigest;
|
||||
|
||||
private final static int POST_MESSAGE_RETRY_NUM = 3;
|
||||
private final static int READ_MESSAGES_RETRY_NUM = 1;
|
||||
private final static int GET_REDUNDANCY_RETRY_NUM = 1;
|
||||
|
||||
private static final int SERVER_THREADPOOL_SIZE = 5;
|
||||
private static final long FAIL_DELAY = 5000;
|
||||
private static final long SUBSCRIPTION_INTERVAL = 10000;
|
||||
|
||||
private int minAbsoluteRedundancy;
|
||||
|
||||
/**
|
||||
* Stores database locations and initializes the web Client
|
||||
* Stores the required minimum redundancy.
|
||||
* Starts the Thread Pool.
|
||||
* @param clientParams contains the required information
|
||||
*/
|
||||
@Override
|
||||
public void init(BulletinBoardClientParams clientParams) {
|
||||
|
||||
super.init(clientParams);
|
||||
|
||||
batchDigest = new GenericBatchDigest(digest);
|
||||
|
||||
minAbsoluteRedundancy = (int) (clientParams.getMinRedundancy() * (float) clientParams.getBulletinBoardAddressCount());
|
||||
|
||||
executorService = Executors.newFixedThreadPool(JOBS_THREAD_NUM);
|
||||
|
||||
clients = new ArrayList<>(clientParams.getBulletinBoardAddressCount());
|
||||
for (String address : clientParams.getBulletinBoardAddressList()){
|
||||
|
||||
SingleServerBulletinBoardClient client =
|
||||
new SingleServerBulletinBoardClient(SERVER_THREADPOOL_SIZE, FAIL_DELAY, SUBSCRIPTION_INTERVAL);
|
||||
|
||||
client.init(BulletinBoardClientParams.newBuilder()
|
||||
.addBulletinBoardAddress(address)
|
||||
.build());
|
||||
|
||||
clients.add(client);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Post message to all DBs
|
||||
* Retry failed DBs
|
||||
* @param msg is the message,
|
||||
* @return the message ID for later retrieval
|
||||
*/
|
||||
@Override
|
||||
public MessageID postMessage(BulletinBoardMessage msg, FutureCallback<Boolean> callback){
|
||||
|
||||
// Create job
|
||||
MultiServerPostMessageWorker worker =
|
||||
new MultiServerPostMessageWorker(clients, minAbsoluteRedundancy, msg, POST_MESSAGE_RETRY_NUM, callback);
|
||||
|
||||
// Submit job
|
||||
executorService.submit(worker);
|
||||
|
||||
// Calculate the correct message ID and return it
|
||||
batchDigest.reset();
|
||||
batchDigest.update(msg.getMsg());
|
||||
return batchDigest.digestAsMessageID();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageID postBatch(CompleteBatch completeBatch, FutureCallback<Boolean> callback) {
|
||||
|
||||
// Create job
|
||||
MultiServerPostBatchWorker worker =
|
||||
new MultiServerPostBatchWorker(clients, minAbsoluteRedundancy, completeBatch, POST_MESSAGE_RETRY_NUM, callback);
|
||||
|
||||
// Submit job
|
||||
executorService.submit(worker);
|
||||
|
||||
// Calculate the correct message ID and return it
|
||||
batchDigest.reset();
|
||||
batchDigest.update(completeBatch);
|
||||
return batchDigest.digestAsMessageID();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginBatch(BeginBatchMessage beginBatchMessage, FutureCallback<Boolean> callback) {
|
||||
|
||||
// Create job
|
||||
MultiServerBeginBatchWorker worker =
|
||||
new MultiServerBeginBatchWorker(clients, minAbsoluteRedundancy, beginBatchMessage, POST_MESSAGE_RETRY_NUM, callback);
|
||||
|
||||
// Submit job
|
||||
executorService.submit(worker);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList,
|
||||
int startPosition, FutureCallback<Boolean> callback) {
|
||||
|
||||
BatchDataContainer batchDataContainer = new BatchDataContainer(signerId, batchId, batchDataList, startPosition);
|
||||
|
||||
// Create job
|
||||
MultiServerPostBatchDataWorker worker =
|
||||
new MultiServerPostBatchDataWorker(clients, minAbsoluteRedundancy, batchDataContainer, POST_MESSAGE_RETRY_NUM, callback);
|
||||
|
||||
// Submit job
|
||||
executorService.submit(worker);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
||||
|
||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList,
|
||||
int startPosition, FutureCallback<Boolean> callback) {
|
||||
|
||||
postBatchData(signerId.toByteArray(), batchId, batchDataList, startPosition, callback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
||||
|
||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeBatch(CloseBatchMessage closeBatchMessage, FutureCallback<Boolean> callback) {
|
||||
|
||||
// Create job
|
||||
MultiServerCloseBatchWorker worker =
|
||||
new MultiServerCloseBatchWorker(clients, minAbsoluteRedundancy, closeBatchMessage, POST_MESSAGE_RETRY_NUM, callback);
|
||||
|
||||
// Submit job
|
||||
executorService.submit(worker);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Access each database and search for a given message ID
|
||||
* Return the number of databases in which the message was found
|
||||
* Only try once per DB
|
||||
* Ignore communication exceptions in specific databases
|
||||
*/
|
||||
@Override
|
||||
public void getRedundancy(MessageID id, FutureCallback<Float> callback) {
|
||||
|
||||
// Create job
|
||||
MultiServerGetRedundancyWorker worker =
|
||||
new MultiServerGetRedundancyWorker(clients, minAbsoluteRedundancy, id, GET_REDUNDANCY_RETRY_NUM, callback);
|
||||
|
||||
// Submit job
|
||||
executorService.submit(worker);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through the DBs and try to retrieve messages according to the specified filter
|
||||
* If at the operation is successful for some DB: return the results and stop iterating
|
||||
* If no operation is successful: return null (NOT blank list)
|
||||
*/
|
||||
@Override
|
||||
public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
|
||||
// Create job
|
||||
MultiServerReadMessagesWorker worker =
|
||||
new MultiServerReadMessagesWorker(clients, minAbsoluteRedundancy, filterList, READ_MESSAGES_RETRY_NUM, callback);
|
||||
|
||||
// Submit job
|
||||
executorService.submit(worker);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback<CompleteBatch> callback) {
|
||||
|
||||
// Create job
|
||||
MultiServerReadBatchWorker worker =
|
||||
new MultiServerReadBatchWorker(clients, minAbsoluteRedundancy, batchSpecificationMessage, READ_MESSAGES_RETRY_NUM, callback);
|
||||
|
||||
// Submit job
|
||||
executorService.submit(worker);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is not supported by this class!
|
||||
* This is because it has no meaning when considering more than one server without knowing which server will be contacted
|
||||
*/
|
||||
@Override
|
||||
public void querySync(SyncQuery syncQuery, FutureCallback<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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,272 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.protobuf.Timestamp;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.util.BulletinBoardUtils;
|
||||
|
||||
import static meerkat.protobuf.BulletinBoardAPI.FilterType.*;
|
||||
|
||||
import java.sql.Time;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 03-Mar-16.
|
||||
* A multi-server implementation of the {@link BulletinBoardSubscriber}
|
||||
*/
|
||||
public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber {
|
||||
|
||||
protected final Collection<SubscriptionAsyncBulletinBoardClient> clients;
|
||||
protected final BulletinBoardClient localClient;
|
||||
|
||||
protected Iterator<SubscriptionAsyncBulletinBoardClient> clientIterator;
|
||||
protected SubscriptionAsyncBulletinBoardClient currentClient;
|
||||
|
||||
private long lastServerSwitchTime;
|
||||
|
||||
private AtomicBoolean isSyncInProgress;
|
||||
private Semaphore rescheduleSemaphore;
|
||||
|
||||
private static final Float[] BREAKPOINTS = {0.5f, 0.75f, 0.9f, 0.95f, 0.99f, 0.999f};
|
||||
|
||||
public ThreadedBulletinBoardSubscriber(Collection<SubscriptionAsyncBulletinBoardClient> clients, BulletinBoardClient localClient) {
|
||||
|
||||
this.clients = clients;
|
||||
this.localClient = localClient;
|
||||
|
||||
lastServerSwitchTime = System.currentTimeMillis();
|
||||
|
||||
clientIterator = clients.iterator();
|
||||
currentClient = clientIterator.next();
|
||||
|
||||
isSyncInProgress = new AtomicBoolean(false);
|
||||
rescheduleSemaphore = new Semaphore(1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves to next client and performs resync with it
|
||||
*/
|
||||
private void nextClient() {
|
||||
|
||||
try {
|
||||
|
||||
rescheduleSemaphore.acquire();
|
||||
|
||||
if (!clientIterator.hasNext()){
|
||||
clientIterator = clients.iterator();
|
||||
}
|
||||
|
||||
currentClient = clientIterator.next();
|
||||
|
||||
lastServerSwitchTime = System.currentTimeMillis();
|
||||
|
||||
isSyncInProgress.set(false);
|
||||
|
||||
rescheduleSemaphore.release();
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
// TODO: log
|
||||
// Do not change client
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private abstract class SubscriberCallback<T> implements FutureCallback<T> {
|
||||
|
||||
protected final MessageFilterList filterList;
|
||||
protected final FutureCallback<List<BulletinBoardMessage>> callback;
|
||||
private final long invocationTime;
|
||||
|
||||
public SubscriberCallback(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
|
||||
this.filterList = filterList;
|
||||
this.callback = callback;
|
||||
this.invocationTime = System.currentTimeMillis();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles resyncing process for the given subscription after a server is switched
|
||||
* Specifically: generates a sync query from the local database and uses it to query the current server
|
||||
*/
|
||||
private void reSync() {
|
||||
|
||||
SyncQuery syncQuery = null;
|
||||
try {
|
||||
|
||||
syncQuery = localClient.generateSyncQuery(GenerateSyncQueryParams.newBuilder()
|
||||
.setFilterList(filterList)
|
||||
.addAllBreakpointList(Arrays.asList(BREAKPOINTS))
|
||||
.build());
|
||||
|
||||
} catch (CommunicationException e) {
|
||||
|
||||
// Handle failure in standard way
|
||||
onFailure(e);
|
||||
|
||||
}
|
||||
|
||||
currentClient.querySync(syncQuery, new SyncQueryCallback(filterList, callback));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reschedules the subscription
|
||||
*/
|
||||
private void reschedule() {
|
||||
|
||||
try {
|
||||
|
||||
rescheduleSemaphore.acquire();
|
||||
|
||||
reSync();
|
||||
|
||||
rescheduleSemaphore.release();
|
||||
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
|
||||
//TODO: log
|
||||
|
||||
callback.onFailure(e); // Hard error: Cannot guarantee subscription safety
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
|
||||
// If server failure is not already known: switch to next client and resync
|
||||
if (invocationTime > lastServerSwitchTime){
|
||||
|
||||
// Make sure only what thread switches the client
|
||||
if (isSyncInProgress.compareAndSet(false, true)){
|
||||
nextClient();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
reschedule();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides handling logic for resync query callback operation
|
||||
* Receives a SyncQueryResponse and reads the missing data (starting from the received timestamp) if needed
|
||||
*/
|
||||
protected class SyncQueryCallback extends SubscriberCallback<SyncQueryResponse> {
|
||||
|
||||
public SyncQueryCallback (MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
|
||||
super(filterList, callback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(SyncQueryResponse result) {
|
||||
|
||||
final Timestamp DEFAULT_TIME = BulletinBoardUtils.toTimestampProto(946728000); // Year 2000
|
||||
|
||||
// Read required messages according to received Timestamp
|
||||
|
||||
Timestamp syncTimestamp;
|
||||
|
||||
if (result.hasLastTimeOfSync()) {
|
||||
syncTimestamp = result.getLastTimeOfSync(); // Use returned time of sync
|
||||
} else {
|
||||
syncTimestamp = DEFAULT_TIME; // Get all messages
|
||||
}
|
||||
|
||||
MessageFilterList timestampedFilterList = filterList.toBuilder()
|
||||
.removeFilter(filterList.getFilterCount()-1) // Remove MIN_ENTRY filter
|
||||
.addFilter(MessageFilter.newBuilder() // Add timestamp filter
|
||||
.setType(AFTER_TIME)
|
||||
.setTimestamp(syncTimestamp)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
currentClient.readMessages(timestampedFilterList, new ReSyncCallback(filterList, callback, result.getLastEntryNum()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides handling logic for callback of resyncing process
|
||||
* Receives the missing messages, handles them and resubscribes
|
||||
*/
|
||||
protected class ReSyncCallback extends SubscriberCallback<List<BulletinBoardMessage>> {
|
||||
|
||||
private long minEntry;
|
||||
|
||||
public ReSyncCallback (MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback, long minEntry) {
|
||||
|
||||
super(filterList, callback);
|
||||
|
||||
this.minEntry = minEntry;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(List<BulletinBoardMessage> result) {
|
||||
|
||||
// Propagate result to caller
|
||||
callback.onSuccess(result);
|
||||
|
||||
// Renew subscription
|
||||
|
||||
MessageFilterList newFilterList = filterList.toBuilder()
|
||||
.removeFilter(filterList.getFilterCount()-1) // Remove current MIN_ENTRY filter
|
||||
.addFilter(MessageFilter.newBuilder() // Add new MIN_ENTRY filter for current server
|
||||
.setType(MIN_ENTRY)
|
||||
.setEntry(minEntry)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
currentClient.subscribe(newFilterList, callback);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the handling logic for results and failures of main subscription (while there are no errors)
|
||||
*/
|
||||
protected class SubscriptionCallback extends SubscriberCallback<List<BulletinBoardMessage>> {
|
||||
|
||||
public SubscriptionCallback(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback){
|
||||
super(filterList, callback);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSuccess(List<BulletinBoardMessage> result) {
|
||||
|
||||
// Propagate result to caller
|
||||
callback.onSuccess(result);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(MessageFilterList filterList, long startEntry, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
|
||||
currentClient.subscribe(filterList, startEntry, new SubscriptionCallback(filterList, callback));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) {
|
||||
subscribe(filterList, 0, callback);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package meerkat.bulletinboard.callbacks;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import meerkat.bulletinboard.BulletinClientJob;
|
||||
import meerkat.bulletinboard.BulletinClientJobResult;
|
||||
import meerkat.bulletinboard.BulletinClientWorker;
|
||||
import meerkat.protobuf.BulletinBoardAPI;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is a future callback used to listen to workers and run on job finish
|
||||
* Depending on the type of job and the finishing status of the worker: a decision is made whether to retry or return an error
|
||||
*/
|
||||
public abstract class ClientFutureCallback implements FutureCallback<BulletinClientJobResult> {
|
||||
|
||||
protected ListeningExecutorService listeningExecutor;
|
||||
|
||||
ClientFutureCallback(ListeningExecutorService listeningExecutor) {
|
||||
this.listeningExecutor = listeningExecutor;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package meerkat.bulletinboard.callbacks;
|
||||
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import meerkat.bulletinboard.BulletinBoardClient;
|
||||
import meerkat.bulletinboard.BulletinClientJobResult;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is a future callback used to listen to workers and run on job finish
|
||||
* Depending on the type of job and the finishing status of the worker: a decision is made whether to retry or return an error
|
||||
*/
|
||||
public class GetRedundancyFutureCallback extends ClientFutureCallback {
|
||||
|
||||
private BulletinBoardClient.ClientCallback<Float> callback;
|
||||
|
||||
public GetRedundancyFutureCallback(ListeningExecutorService listeningExecutor,
|
||||
BulletinBoardClient.ClientCallback<Float> callback) {
|
||||
super(listeningExecutor);
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(BulletinClientJobResult result) {
|
||||
|
||||
int absoluteRedundancy = ((IntMsg) result.getResult()).getValue();
|
||||
int totalServers = result.getJob().getServerAddresses().size();
|
||||
|
||||
callback.handleCallback( ((float) absoluteRedundancy) / ((float) totalServers) );
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
callback.handleFailure(t);
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package meerkat.bulletinboard.callbacks;
|
||||
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import meerkat.bulletinboard.BulletinBoardClient;
|
||||
import meerkat.bulletinboard.BulletinClientJob;
|
||||
import meerkat.bulletinboard.BulletinClientJobResult;
|
||||
import meerkat.bulletinboard.BulletinClientWorker;
|
||||
import meerkat.protobuf.BulletinBoardAPI;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is a future callback used to listen to workers and run on job finish
|
||||
* Depending on the type of job and the finishing status of the worker: a decision is made whether to retry or return an error
|
||||
*/
|
||||
public class PostMessageFutureCallback extends ClientFutureCallback {
|
||||
|
||||
private BulletinBoardClient.ClientCallback<?> callback;
|
||||
|
||||
public PostMessageFutureCallback(ListeningExecutorService listeningExecutor,
|
||||
BulletinBoardClient.ClientCallback<?> callback) {
|
||||
super(listeningExecutor);
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(BulletinClientJobResult result) {
|
||||
|
||||
BulletinClientJob job = result.getJob();
|
||||
|
||||
job.decMaxRetry();
|
||||
|
||||
// If redundancy is below threshold: retry
|
||||
if (job.getMinServers() > 0 && job.isRetry()) {
|
||||
Futures.addCallback(listeningExecutor.submit(new BulletinClientWorker(job)), this);
|
||||
}
|
||||
|
||||
callback.handleCallback(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
callback.handleFailure(t);
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package meerkat.bulletinboard.callbacks;
|
||||
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import meerkat.bulletinboard.BulletinBoardClient;
|
||||
import meerkat.bulletinboard.BulletinClientJob;
|
||||
import meerkat.bulletinboard.BulletinClientJobResult;
|
||||
import meerkat.bulletinboard.BulletinClientWorker;
|
||||
import meerkat.protobuf.BulletinBoardAPI;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is a future callback used to listen to workers and run on job finish
|
||||
* Depending on the type of job and the finishing status of the worker: a decision is made whether to retry or return an error
|
||||
*/
|
||||
public class ReadMessagesFutureCallback extends ClientFutureCallback {
|
||||
|
||||
private BulletinBoardClient.ClientCallback<List<BulletinBoardAPI.BulletinBoardMessage>> callback;
|
||||
|
||||
public ReadMessagesFutureCallback(ListeningExecutorService listeningExecutor,
|
||||
BulletinBoardClient.ClientCallback<List<BulletinBoardAPI.BulletinBoardMessage>> callback) {
|
||||
super(listeningExecutor);
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(BulletinClientJobResult result) {
|
||||
|
||||
callback.handleCallback(((BulletinBoardAPI.BulletinBoardMessageList) result.getResult()).getMessageList());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
callback.handleFailure(t);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package meerkat.bulletinboard.workers.multiserver;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import meerkat.bulletinboard.SingleServerBulletinBoardClient;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BeginBatchMessage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
*/
|
||||
public class MultiServerBeginBatchWorker extends MultiServerGenericPostWorker<BeginBatchMessage> {
|
||||
|
||||
public MultiServerBeginBatchWorker(List<SingleServerBulletinBoardClient> clients,
|
||||
int minServers, BeginBatchMessage payload, int maxRetry,
|
||||
FutureCallback<Boolean> futureCallback) {
|
||||
|
||||
super(clients, minServers, payload, maxRetry, futureCallback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(SingleServerBulletinBoardClient client, BeginBatchMessage payload) {
|
||||
client.beginBatch(payload, this);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package meerkat.bulletinboard.workers.multiserver;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import meerkat.bulletinboard.SingleServerBulletinBoardClient;
|
||||
import meerkat.protobuf.BulletinBoardAPI.CloseBatchMessage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
*/
|
||||
public class MultiServerCloseBatchWorker extends MultiServerGenericPostWorker<CloseBatchMessage> {
|
||||
|
||||
public MultiServerCloseBatchWorker(List<SingleServerBulletinBoardClient> clients,
|
||||
int minServers, CloseBatchMessage payload, int maxRetry,
|
||||
FutureCallback<Boolean> futureCallback) {
|
||||
|
||||
super(clients, minServers, payload, maxRetry, futureCallback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(SingleServerBulletinBoardClient client, CloseBatchMessage payload) {
|
||||
client.closeBatch(payload, this);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package meerkat.bulletinboard.workers.multiserver;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import meerkat.bulletinboard.MultiServerWorker;
|
||||
import meerkat.bulletinboard.SingleServerBulletinBoardClient;
|
||||
import meerkat.comm.CommunicationException;
|
||||
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
*/
|
||||
public abstract class MultiServerGenericPostWorker<T> extends MultiServerWorker<T, Boolean> {
|
||||
|
||||
public MultiServerGenericPostWorker(List<SingleServerBulletinBoardClient> clients,
|
||||
int minServers, T payload, int maxRetry,
|
||||
FutureCallback<Boolean> futureCallback) {
|
||||
|
||||
super(clients, minServers, payload, maxRetry, futureCallback);
|
||||
|
||||
}
|
||||
|
||||
protected abstract void doPost(SingleServerBulletinBoardClient client, T payload);
|
||||
|
||||
/**
|
||||
* This method carries out the actual communication with the servers via HTTP Post
|
||||
* It accesses the servers one by one and tries to post the payload to each in turn
|
||||
* The method will only iterate once through the server list
|
||||
* Successful post to a server results in removing the server from the list
|
||||
*/
|
||||
public void run() {
|
||||
|
||||
// Iterate through servers
|
||||
|
||||
Iterator<SingleServerBulletinBoardClient> clientIterator = getClientIterator();
|
||||
|
||||
while (clientIterator.hasNext()) {
|
||||
|
||||
// Send request to Server
|
||||
SingleServerBulletinBoardClient client = clientIterator.next();
|
||||
|
||||
doPost(client, payload);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
if (result){
|
||||
if (minServers.decrementAndGet() <= 0){
|
||||
succeed(Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
if (maxFailedServers.decrementAndGet() < 0){
|
||||
fail(t);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package meerkat.bulletinboard.workers.multiserver;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import meerkat.bulletinboard.MultiServerWorker;
|
||||
import meerkat.bulletinboard.SingleServerBulletinBoardClient;
|
||||
import meerkat.comm.CommunicationException;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
*/
|
||||
public abstract class MultiServerGenericReadWorker<IN, OUT> extends MultiServerWorker<IN, OUT>{
|
||||
|
||||
private final Iterator<SingleServerBulletinBoardClient> clientIterator;
|
||||
|
||||
public MultiServerGenericReadWorker(List<SingleServerBulletinBoardClient> clients,
|
||||
int minServers, IN payload, int maxRetry,
|
||||
FutureCallback<OUT> futureCallback) {
|
||||
|
||||
super(clients, true, minServers, payload, maxRetry, futureCallback); // Shuffle clients on creation to balance load
|
||||
|
||||
clientIterator = getClientIterator();
|
||||
|
||||
}
|
||||
|
||||
protected abstract void doRead(IN payload, SingleServerBulletinBoardClient client);
|
||||
|
||||
/**
|
||||
* This method carries out the actual communication with the servers via HTTP Post
|
||||
* It accesses the servers in a random order until one answers it
|
||||
* Successful retrieval from any server terminates the method and returns the received values; The list is not changed
|
||||
*/
|
||||
public void run(){
|
||||
|
||||
// Iterate through servers
|
||||
|
||||
if (clientIterator.hasNext()) {
|
||||
|
||||
// Get next server
|
||||
SingleServerBulletinBoardClient client = clientIterator.next();
|
||||
|
||||
// Retrieve answer from server
|
||||
doRead(payload, client);
|
||||
|
||||
} else {
|
||||
fail(new CommunicationException("Could not contact any server"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(OUT msg) {
|
||||
succeed(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
run(); // Retry with next server
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package meerkat.bulletinboard.workers.multiserver;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import meerkat.bulletinboard.MultiServerWorker;
|
||||
import meerkat.bulletinboard.SingleServerBulletinBoardClient;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
*/
|
||||
public class MultiServerGetRedundancyWorker extends MultiServerWorker<MessageID, Float> {
|
||||
|
||||
private AtomicInteger serversContainingMessage;
|
||||
private AtomicInteger totalContactedServers;
|
||||
|
||||
public MultiServerGetRedundancyWorker(List<SingleServerBulletinBoardClient> clients,
|
||||
int minServers, MessageID payload, int maxRetry,
|
||||
FutureCallback<Float> futureCallback) {
|
||||
|
||||
super(clients, minServers, payload, maxRetry, futureCallback); // Shuffle clients on creation to balance load
|
||||
|
||||
serversContainingMessage = new AtomicInteger(0);
|
||||
totalContactedServers = new AtomicInteger(0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method carries out the actual communication with the servers via HTTP Post
|
||||
* It accesses the servers in a random order until one answers it
|
||||
* Successful retrieval from any server terminates the method and returns the received values; The list is not changed
|
||||
*/
|
||||
public void run(){
|
||||
|
||||
Iterator<SingleServerBulletinBoardClient> clientIterator = getClientIterator();
|
||||
|
||||
// Iterate through clients
|
||||
|
||||
while (clientIterator.hasNext()) {
|
||||
|
||||
SingleServerBulletinBoardClient client = clientIterator.next();
|
||||
|
||||
// Send request to client
|
||||
client.getRedundancy(payload,this);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Float result) {
|
||||
|
||||
if (result > 0.5) {
|
||||
serversContainingMessage.incrementAndGet();
|
||||
}
|
||||
|
||||
if (totalContactedServers.incrementAndGet() >= getClientNumber()){
|
||||
succeed(((float) serversContainingMessage.get()) / ((float) getClientNumber()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
onSuccess(0.0f);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package meerkat.bulletinboard.workers.multiserver;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import meerkat.bulletinboard.SingleServerBulletinBoardClient;
|
||||
import meerkat.bulletinboard.BatchDataContainer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
*/
|
||||
public class MultiServerPostBatchDataWorker extends MultiServerGenericPostWorker<BatchDataContainer> {
|
||||
|
||||
public MultiServerPostBatchDataWorker(List<SingleServerBulletinBoardClient> clients,
|
||||
int minServers, BatchDataContainer payload, int maxRetry,
|
||||
FutureCallback<Boolean> futureCallback) {
|
||||
|
||||
super(clients, minServers, payload, maxRetry, futureCallback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(SingleServerBulletinBoardClient client, BatchDataContainer payload) {
|
||||
client.postBatchData(payload.signerId, payload.batchId, payload.batchDataList, payload.startPosition, this);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package meerkat.bulletinboard.workers.multiserver;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import meerkat.bulletinboard.CompleteBatch;
|
||||
import meerkat.bulletinboard.SingleServerBulletinBoardClient;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
*/
|
||||
public class MultiServerPostBatchWorker extends MultiServerGenericPostWorker<CompleteBatch> {
|
||||
|
||||
public MultiServerPostBatchWorker(List<SingleServerBulletinBoardClient> clients,
|
||||
int minServers, CompleteBatch payload, int maxRetry,
|
||||
FutureCallback<Boolean> futureCallback) {
|
||||
|
||||
super(clients, minServers, payload, maxRetry, futureCallback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(SingleServerBulletinBoardClient client, CompleteBatch payload) {
|
||||
client.postBatch(payload, this);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
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 MultiServerPostMessageWorker extends MultiServerGenericPostWorker<BulletinBoardMessage> {
|
||||
|
||||
public MultiServerPostMessageWorker(List<SingleServerBulletinBoardClient> clients,
|
||||
int minServers, BulletinBoardMessage payload, int maxRetry,
|
||||
FutureCallback<Boolean> futureCallback) {
|
||||
|
||||
super(clients, minServers, payload, maxRetry, futureCallback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(SingleServerBulletinBoardClient client, BulletinBoardMessage payload) {
|
||||
client.postMessage(payload, this);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
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,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 MultiServerReadMessagesWorker extends MultiServerGenericReadWorker<MessageFilterList,List<BulletinBoardMessage>>{
|
||||
|
||||
public MultiServerReadMessagesWorker(List<SingleServerBulletinBoardClient> clients,
|
||||
int minServers, MessageFilterList payload, int maxRetry,
|
||||
FutureCallback<List<BulletinBoardMessage>> futureCallback) {
|
||||
|
||||
super(clients, minServers, payload, maxRetry, futureCallback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRead(MessageFilterList payload, SingleServerBulletinBoardClient client) {
|
||||
client.readMessages(payload, this);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package meerkat.bulletinboard.workers.singleserver;
|
||||
|
||||
import meerkat.protobuf.BulletinBoardAPI.BeginBatchMessage;
|
||||
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.BEGIN_BATCH_PATH;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
* Tries to contact server once and perform a post operation
|
||||
*/
|
||||
public class SingleServerBeginBatchWorker extends SingleServerGenericPostWorker<BeginBatchMessage> {
|
||||
|
||||
public SingleServerBeginBatchWorker(String serverAddress, BeginBatchMessage payload, int maxRetry) {
|
||||
super(serverAddress, BEGIN_BATCH_PATH, payload, maxRetry);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package meerkat.bulletinboard.workers.singleserver;
|
||||
|
||||
import meerkat.protobuf.BulletinBoardAPI.CloseBatchMessage;
|
||||
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.CLOSE_BATCH_PATH;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
* Tries to contact server once and perform a stop batch operation
|
||||
*/
|
||||
public class SingleServerCloseBatchWorker extends SingleServerGenericPostWorker<CloseBatchMessage> {
|
||||
|
||||
public SingleServerCloseBatchWorker(String serverAddress, CloseBatchMessage payload, int maxRetry) {
|
||||
super(serverAddress, CLOSE_BATCH_PATH, payload, maxRetry);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package meerkat.bulletinboard.workers.singleserver;
|
||||
|
||||
import com.google.protobuf.BoolValue;
|
||||
import meerkat.bulletinboard.SingleServerWorker;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.protobuf.Comm.*;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
* Tries to contact server once and perform a post operation
|
||||
*/
|
||||
public class SingleServerGenericPostWorker<T> extends SingleServerWorker<T, Boolean> {
|
||||
|
||||
private final String subPath;
|
||||
|
||||
public SingleServerGenericPostWorker(String serverAddress, String subPath, T payload, int maxRetry) {
|
||||
super(serverAddress, payload, maxRetry);
|
||||
this.subPath = subPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method carries out the actual communication with the server via HTTP Post
|
||||
* It accesses the server and tries to post the payload to it
|
||||
* Successful post to a server results
|
||||
* @return TRUE if the operation is successful
|
||||
* @throws CommunicationException if the operation is unsuccessful
|
||||
*/
|
||||
public Boolean call() throws CommunicationException{
|
||||
|
||||
Client client = clientLocal.get();
|
||||
|
||||
WebTarget webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(subPath);
|
||||
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(
|
||||
Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF));
|
||||
|
||||
try {
|
||||
|
||||
// If a BoolValue entity is returned: the post was successful
|
||||
response.readEntity(BoolValue.class);
|
||||
return Boolean.TRUE;
|
||||
|
||||
} catch (ProcessingException | IllegalStateException e) {
|
||||
|
||||
// Post to this server failed
|
||||
throw new CommunicationException("Could not contact the server");
|
||||
|
||||
}
|
||||
finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package meerkat.bulletinboard.workers.singleserver;
|
||||
|
||||
import meerkat.bulletinboard.SingleServerWorker;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.comm.MessageInputStream;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.rest.Constants;
|
||||
|
||||
import javax.ws.rs.ProcessingException;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH;
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.READ_MESSAGES_PATH;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
*/
|
||||
public class SingleServerGetRedundancyWorker extends SingleServerWorker<MessageID, Float> {
|
||||
|
||||
public SingleServerGetRedundancyWorker(String serverAddress, MessageID payload, int maxRetry) {
|
||||
super(serverAddress, payload, maxRetry);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method carries out the actual communication with the server via HTTP Post
|
||||
* It queries the server for a message with the given ID
|
||||
* @return TRUE if the message exists in the server and FALSE otherwise
|
||||
* @throws CommunicationException if the server does not return a valid answer
|
||||
*/
|
||||
public Float call() throws CommunicationException{
|
||||
|
||||
Client client = clientLocal.get();
|
||||
|
||||
WebTarget webTarget;
|
||||
Response response;
|
||||
|
||||
MessageFilterList msgFilterList = MessageFilterList.newBuilder()
|
||||
.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.MSG_ID)
|
||||
.setId(payload.getID())
|
||||
.build()
|
||||
).build();
|
||||
|
||||
// Send request to Server
|
||||
|
||||
// Send request to Server
|
||||
webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH);
|
||||
InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msgFilterList, Constants.MEDIATYPE_PROTOBUF), InputStream.class);
|
||||
|
||||
MessageInputStream<BulletinBoardMessage> inputStream = null;
|
||||
|
||||
// Retrieve answer
|
||||
|
||||
try {
|
||||
|
||||
inputStream = MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BulletinBoardMessage.class);
|
||||
|
||||
if (inputStream.asList().size() > 0){
|
||||
// Message exists in the server
|
||||
return 1.0f;
|
||||
}
|
||||
else {
|
||||
// Message does not exist in the server
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
// Read failed
|
||||
throw new CommunicationException("Server access failed");
|
||||
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package meerkat.bulletinboard.workers.singleserver;
|
||||
|
||||
import meerkat.protobuf.BulletinBoardAPI.BatchMessage;
|
||||
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.POST_BATCH_PATH;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
* Tries to contact server once and perform a post batch operation
|
||||
*/
|
||||
public class SingleServerPostBatchWorker extends SingleServerGenericPostWorker<BatchMessage> {
|
||||
|
||||
public SingleServerPostBatchWorker(String serverAddress, BatchMessage payload, int maxRetry) {
|
||||
super(serverAddress, POST_BATCH_PATH, payload, maxRetry);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package meerkat.bulletinboard.workers.singleserver;
|
||||
|
||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.POST_MESSAGE_PATH;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
* Tries to contact server once and perform a post operation
|
||||
*/
|
||||
public class SingleServerPostMessageWorker extends SingleServerGenericPostWorker<BulletinBoardMessage> {
|
||||
|
||||
public SingleServerPostMessageWorker(String serverAddress, BulletinBoardMessage payload, int maxRetry) {
|
||||
super(serverAddress, POST_MESSAGE_PATH, payload, maxRetry);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package meerkat.bulletinboard.workers.singleserver;
|
||||
|
||||
import meerkat.bulletinboard.SingleServerWorker;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.protobuf.BulletinBoardAPI.SyncQuery;
|
||||
import meerkat.protobuf.BulletinBoardAPI.SyncQueryResponse;
|
||||
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.SYNC_QUERY_PATH;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
* Tries to contact server once and perform a post operation
|
||||
*/
|
||||
public class SingleServerQuerySyncWorker extends SingleServerWorker<SyncQuery, SyncQueryResponse> {
|
||||
|
||||
public SingleServerQuerySyncWorker(String serverAddress, SyncQuery payload, int maxRetry) {
|
||||
super(serverAddress, payload, maxRetry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SyncQueryResponse call() throws Exception {
|
||||
|
||||
Client client = clientLocal.get();
|
||||
|
||||
WebTarget webTarget;
|
||||
Response response;
|
||||
|
||||
// Send request to Server
|
||||
|
||||
webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(SYNC_QUERY_PATH);
|
||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF));
|
||||
|
||||
// Retrieve answer
|
||||
|
||||
try {
|
||||
|
||||
// If a BulletinBoardMessageList is returned: the read was successful
|
||||
return response.readEntity(SyncQueryResponse.class);
|
||||
|
||||
} catch (ProcessingException | IllegalStateException e) {
|
||||
|
||||
// Read failed
|
||||
throw new CommunicationException("Server access failed");
|
||||
|
||||
}
|
||||
finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package meerkat.bulletinboard.workers.singleserver;
|
||||
|
||||
import meerkat.bulletinboard.CompleteBatch;
|
||||
import meerkat.bulletinboard.SingleServerWorker;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.comm.MessageInputStream;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.rest.Constants;
|
||||
|
||||
import javax.ws.rs.ProcessingException;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.GenericType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH;
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.READ_MESSAGES_PATH;
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.READ_BATCH_PATH;
|
||||
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.BATCH_ID_TAG_PREFIX;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
*/
|
||||
public class SingleServerReadBatchWorker extends SingleServerWorker<BatchSpecificationMessage, List<BatchData>> {
|
||||
|
||||
public SingleServerReadBatchWorker(String serverAddress, BatchSpecificationMessage payload, int maxRetry) {
|
||||
super(serverAddress, payload, maxRetry);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method carries out the actual communication with the server via HTTP Post
|
||||
* Upon successful retrieval from the server the method returns the received values
|
||||
* @return the complete batch as read from the server
|
||||
* @throws CommunicationException if the server's response is invalid
|
||||
*/
|
||||
public List<BatchData> call() throws CommunicationException{
|
||||
|
||||
Client client = clientLocal.get();
|
||||
|
||||
WebTarget webTarget;
|
||||
|
||||
// Get the batch data
|
||||
|
||||
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);
|
||||
|
||||
MessageInputStream<BatchData> inputStream = null;
|
||||
|
||||
try {
|
||||
|
||||
inputStream = MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BatchData.class);
|
||||
|
||||
return inputStream.asList();
|
||||
|
||||
} catch (IOException | InvocationTargetException e) {
|
||||
|
||||
// Read failed
|
||||
throw new CommunicationException("Could not contact the server or server returned illegal result");
|
||||
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
|
||||
throw new CommunicationException("MessageInputStream error");
|
||||
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package meerkat.bulletinboard.workers.singleserver;
|
||||
|
||||
import meerkat.bulletinboard.SingleServerWorker;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.comm.MessageInputStream;
|
||||
import meerkat.protobuf.BulletinBoardAPI;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessageList;
|
||||
import meerkat.protobuf.BulletinBoardAPI.MessageFilterList;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||
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 java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH;
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.READ_MESSAGES_PATH;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||
*/
|
||||
public class SingleServerReadMessagesWorker extends SingleServerWorker<MessageFilterList, List<BulletinBoardMessage>> {
|
||||
|
||||
public SingleServerReadMessagesWorker(String serverAddress, MessageFilterList payload, int maxRetry) {
|
||||
super(serverAddress, payload, maxRetry);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method carries out the actual communication with the server via HTTP Post
|
||||
* Upon successful retrieval from the server the method returns the received values
|
||||
* @return The list of messages returned by the server
|
||||
* @throws CommunicationException if the server's response is invalid
|
||||
*/
|
||||
public List<BulletinBoardMessage> call() throws CommunicationException{
|
||||
|
||||
Client client = clientLocal.get();
|
||||
|
||||
WebTarget webTarget;
|
||||
|
||||
// Send request to Server
|
||||
webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH);
|
||||
InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF), InputStream.class);
|
||||
|
||||
MessageInputStream<BulletinBoardMessage> inputStream = null;
|
||||
|
||||
try {
|
||||
|
||||
inputStream = MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BulletinBoardMessage.class);
|
||||
|
||||
return inputStream.asList();
|
||||
|
||||
} catch (IOException | InvocationTargetException e) {
|
||||
|
||||
// Read failed
|
||||
throw new CommunicationException("Could not contact the server or server returned illegal result");
|
||||
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
|
||||
throw new CommunicationException("MessageInputStream error");
|
||||
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,214 +0,0 @@
|
|||
import com.google.protobuf.ByteString;
|
||||
import meerkat.bulletinboard.BulletinBoardClient;
|
||||
import meerkat.bulletinboard.BulletinBoardClient.ClientCallback;
|
||||
import meerkat.bulletinboard.ThreadedBulletinBoardClient;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.protobuf.Crypto;
|
||||
|
||||
import meerkat.protobuf.Voting.*;
|
||||
import meerkat.util.BulletinBoardMessageComparator;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.number.OrderingComparison.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 05-Dec-15.
|
||||
*/
|
||||
public class BulletinBoardClientIntegrationTest {
|
||||
|
||||
Semaphore jobSemaphore;
|
||||
Vector<Throwable> thrown;
|
||||
|
||||
private class PostCallback implements ClientCallback<Object>{
|
||||
|
||||
@Override
|
||||
public void handleCallback(Object msg) {
|
||||
System.err.println("Post operation completed");
|
||||
jobSemaphore.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFailure(Throwable t) {
|
||||
thrown.add(t);
|
||||
jobSemaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
private class RedundancyCallback implements ClientCallback<Float>{
|
||||
|
||||
private float minRedundancy;
|
||||
|
||||
public RedundancyCallback(float minRedundancy) {
|
||||
this.minRedundancy = minRedundancy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCallback(Float redundancy) {
|
||||
System.err.println("Redundancy found is: " + redundancy);
|
||||
jobSemaphore.release();
|
||||
assertThat(redundancy, greaterThanOrEqualTo(minRedundancy));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFailure(Throwable t) {
|
||||
thrown.add(t);
|
||||
jobSemaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
private class ReadCallback implements ClientCallback<List<BulletinBoardMessage>>{
|
||||
|
||||
private List<BulletinBoardMessage> expectedMsgList;
|
||||
|
||||
public ReadCallback(List<BulletinBoardMessage> expectedMsgList) {
|
||||
this.expectedMsgList = expectedMsgList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCallback(List<BulletinBoardMessage> messages) {
|
||||
|
||||
System.err.println(messages);
|
||||
jobSemaphore.release();
|
||||
|
||||
BulletinBoardMessageComparator msgComparator = new BulletinBoardMessageComparator();
|
||||
|
||||
assertThat(messages.size(), is(expectedMsgList.size()));
|
||||
|
||||
Iterator<BulletinBoardMessage> expectedMessageIterator = expectedMsgList.iterator();
|
||||
Iterator<BulletinBoardMessage> receivedMessageIterator = messages.iterator();
|
||||
|
||||
while (expectedMessageIterator.hasNext()) {
|
||||
assertThat(msgComparator.compare(expectedMessageIterator.next(), receivedMessageIterator.next()), is(0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFailure(Throwable t) {
|
||||
thrown.add(t);
|
||||
jobSemaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
private BulletinBoardClient bulletinBoardClient;
|
||||
|
||||
private PostCallback postCallback;
|
||||
private RedundancyCallback redundancyCallback;
|
||||
private ReadCallback readCallback;
|
||||
|
||||
private static String PROP_GETTY_URL = "gretty.httpBaseURI";
|
||||
private static String DEFAULT_BASE_URL = "http://localhost:8081";
|
||||
private static String BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL);
|
||||
|
||||
@Before
|
||||
public void init(){
|
||||
|
||||
bulletinBoardClient = new ThreadedBulletinBoardClient();
|
||||
|
||||
List<String> testDB = new LinkedList<String>();
|
||||
testDB.add(BASE_URL);
|
||||
|
||||
bulletinBoardClient.init(BulletinBoardClientParams.newBuilder()
|
||||
.addBulletinBoardAddress("http://localhost:8081")
|
||||
.setMinRedundancy((float) 1.0)
|
||||
.build());
|
||||
|
||||
postCallback = new PostCallback();
|
||||
redundancyCallback = new RedundancyCallback((float) 1.0);
|
||||
|
||||
thrown = new Vector<>();
|
||||
jobSemaphore = new Semaphore(0);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postTest() {
|
||||
|
||||
byte[] b1 = {(byte) 1, (byte) 2, (byte) 3, (byte) 4};
|
||||
byte[] b2 = {(byte) 11, (byte) 12, (byte) 13, (byte) 14};
|
||||
byte[] b3 = {(byte) 21, (byte) 22, (byte) 23, (byte) 24};
|
||||
byte[] b4 = {(byte) 4, (byte) 5, (byte) 100, (byte) -50, (byte) 0};
|
||||
|
||||
BulletinBoardMessage msg;
|
||||
|
||||
MessageFilterList filterList;
|
||||
List<BulletinBoardMessage> msgList;
|
||||
|
||||
MessageID messageID;
|
||||
|
||||
Comparator<BulletinBoardMessage> msgComparator = new BulletinBoardMessageComparator();
|
||||
|
||||
msg = BulletinBoardMessage.newBuilder()
|
||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||
.addTag("Signature")
|
||||
.addTag("Trustee")
|
||||
.setData(ByteString.copyFrom(b1))
|
||||
.build())
|
||||
.addSig(Crypto.Signature.newBuilder()
|
||||
.setType(Crypto.SignatureType.DSA)
|
||||
.setData(ByteString.copyFrom(b2))
|
||||
.setSignerId(ByteString.copyFrom(b3))
|
||||
.build())
|
||||
.addSig(Crypto.Signature.newBuilder()
|
||||
.setType(Crypto.SignatureType.ECDSA)
|
||||
.setData(ByteString.copyFrom(b3))
|
||||
.setSignerId(ByteString.copyFrom(b2))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
messageID = bulletinBoardClient.postMessage(msg,postCallback);
|
||||
|
||||
try {
|
||||
jobSemaphore.acquire();
|
||||
} catch (InterruptedException e) {
|
||||
System.err.println(e.getCause() + " " + e.getMessage());
|
||||
}
|
||||
|
||||
bulletinBoardClient.getRedundancy(messageID,redundancyCallback);
|
||||
|
||||
filterList = MessageFilterList.newBuilder()
|
||||
.addFilter(
|
||||
MessageFilter.newBuilder()
|
||||
.setType(FilterType.TAG)
|
||||
.setTag("Signature")
|
||||
.build()
|
||||
)
|
||||
.addFilter(
|
||||
MessageFilter.newBuilder()
|
||||
.setType(FilterType.TAG)
|
||||
.setTag("Trustee")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
msgList = new LinkedList<BulletinBoardMessage>();
|
||||
msgList.add(msg);
|
||||
|
||||
readCallback = new ReadCallback(msgList);
|
||||
|
||||
bulletinBoardClient.readMessages(filterList, readCallback);
|
||||
try {
|
||||
jobSemaphore.acquire(2);
|
||||
} catch (InterruptedException e) {
|
||||
System.err.println(e.getCause() + " " + e.getMessage());
|
||||
}
|
||||
|
||||
bulletinBoardClient.close();
|
||||
|
||||
for (Throwable t : thrown) {
|
||||
System.err.println(t.getMessage());
|
||||
}
|
||||
if (thrown.size() > 0) {
|
||||
assert false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,519 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Timestamp;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.crypto.concrete.ECDSASignature;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import meerkat.util.BulletinBoardMessageComparator;
|
||||
import meerkat.util.BulletinBoardMessageGenerator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 05-Dec-15.
|
||||
*/
|
||||
public class GenericBulletinBoardClientTester {
|
||||
|
||||
// Signature resources
|
||||
|
||||
private GenericBatchDigitalSignature signers[];
|
||||
private ByteString[] signerIDs;
|
||||
|
||||
private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
|
||||
private static String KEYFILE_EXAMPLE3 = "/certs/enduser-certs/user3-key-with-password-shh.p12";
|
||||
|
||||
private static String KEYFILE_PASSWORD1 = "secret";
|
||||
private static String KEYFILE_PASSWORD3 = "shh";
|
||||
|
||||
private static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt";
|
||||
private static String CERT3_PEM_EXAMPLE = "/certs/enduser-certs/user3.crt";
|
||||
|
||||
// Client and callbacks
|
||||
|
||||
private AsyncBulletinBoardClient bulletinBoardClient;
|
||||
|
||||
private PostCallback postCallback;
|
||||
private PostCallback failPostCallback = new PostCallback(true,false);
|
||||
|
||||
private RedundancyCallback redundancyCallback;
|
||||
private ReadCallback readCallback;
|
||||
private ReadBatchCallback readBatchCallback;
|
||||
|
||||
// Sync and misc
|
||||
|
||||
private Semaphore jobSemaphore;
|
||||
private Vector<Throwable> thrown;
|
||||
private Random random;
|
||||
|
||||
// Constructor
|
||||
|
||||
public GenericBulletinBoardClientTester(AsyncBulletinBoardClient bulletinBoardClient){
|
||||
|
||||
this.bulletinBoardClient = bulletinBoardClient;
|
||||
|
||||
signers = new GenericBatchDigitalSignature[2];
|
||||
signerIDs = new ByteString[signers.length];
|
||||
signers[0] = new GenericBatchDigitalSignature(new ECDSASignature());
|
||||
signers[1] = new GenericBatchDigitalSignature(new ECDSASignature());
|
||||
|
||||
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
||||
char[] password = KEYFILE_PASSWORD1.toCharArray();
|
||||
|
||||
KeyStore.Builder keyStoreBuilder;
|
||||
try {
|
||||
keyStoreBuilder = signers[0].getPKCS12KeyStoreBuilder(keyStream, password);
|
||||
|
||||
signers[0].loadSigningCertificate(keyStoreBuilder);
|
||||
|
||||
signers[0].loadVerificationCertificates(getClass().getResourceAsStream(CERT1_PEM_EXAMPLE));
|
||||
|
||||
keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE3);
|
||||
password = KEYFILE_PASSWORD3.toCharArray();
|
||||
|
||||
keyStoreBuilder = signers[1].getPKCS12KeyStoreBuilder(keyStream, password);
|
||||
signers[1].loadSigningCertificate(keyStoreBuilder);
|
||||
|
||||
signers[1].loadVerificationCertificates(getClass().getResourceAsStream(CERT3_PEM_EXAMPLE));
|
||||
|
||||
for (int i = 0 ; i < signers.length ; i++) {
|
||||
signerIDs[i] = signers[i].getSignerID();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed reading from signature file " + e.getMessage());
|
||||
fail("Failed reading from signature file " + e.getMessage());
|
||||
} catch (CertificateException e) {
|
||||
System.err.println("Failed reading certificate " + e.getMessage());
|
||||
fail("Failed reading certificate " + e.getMessage());
|
||||
} catch (KeyStoreException e) {
|
||||
System.err.println("Failed reading keystore " + e.getMessage());
|
||||
fail("Failed reading keystore " + e.getMessage());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
System.err.println("Couldn't find signing algorithm " + e.getMessage());
|
||||
fail("Couldn't find signing algorithm " + e.getMessage());
|
||||
} catch (UnrecoverableKeyException e) {
|
||||
System.err.println("Couldn't find signing key " + e.getMessage());
|
||||
fail("Couldn't find signing key " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Callback definitions
|
||||
|
||||
protected void genericHandleFailure(Throwable t){
|
||||
System.err.println(t.getCause() + " " + t.getMessage());
|
||||
thrown.add(t);
|
||||
jobSemaphore.release();
|
||||
}
|
||||
|
||||
private class PostCallback implements FutureCallback<Boolean>{
|
||||
|
||||
private boolean isAssert;
|
||||
private boolean assertValue;
|
||||
|
||||
public PostCallback() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public PostCallback(boolean isAssert) {
|
||||
this(isAssert,true);
|
||||
}
|
||||
|
||||
public PostCallback(boolean isAssert, boolean assertValue) {
|
||||
this.isAssert = isAssert;
|
||||
this.assertValue = assertValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Boolean msg) {
|
||||
System.err.println("Post operation completed");
|
||||
jobSemaphore.release();
|
||||
//TODO: Change Assert mechanism to exception one
|
||||
if (isAssert) {
|
||||
if (assertValue) {
|
||||
assertThat("Post operation failed", msg, is(Boolean.TRUE));
|
||||
} else {
|
||||
assertThat("Post operation succeeded unexpectedly", msg, is(Boolean.FALSE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
genericHandleFailure(t);
|
||||
}
|
||||
}
|
||||
|
||||
private class RedundancyCallback implements FutureCallback<Float>{
|
||||
|
||||
private float minRedundancy;
|
||||
|
||||
public RedundancyCallback(float minRedundancy) {
|
||||
this.minRedundancy = minRedundancy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Float redundancy) {
|
||||
System.err.println("Redundancy found is: " + redundancy);
|
||||
jobSemaphore.release();
|
||||
assertThat(redundancy, greaterThanOrEqualTo(minRedundancy));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
genericHandleFailure(t);
|
||||
}
|
||||
}
|
||||
|
||||
private class ReadCallback implements FutureCallback<List<BulletinBoardMessage>>{
|
||||
|
||||
private List<BulletinBoardMessage> expectedMsgList;
|
||||
|
||||
public ReadCallback(List<BulletinBoardMessage> expectedMsgList) {
|
||||
this.expectedMsgList = expectedMsgList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(List<BulletinBoardMessage> messages) {
|
||||
|
||||
System.err.println(messages);
|
||||
jobSemaphore.release();
|
||||
|
||||
BulletinBoardMessageComparator msgComparator = new BulletinBoardMessageComparator();
|
||||
|
||||
assertThat(messages.size(), is(expectedMsgList.size()));
|
||||
|
||||
Iterator<BulletinBoardMessage> expectedMessageIterator = expectedMsgList.iterator();
|
||||
Iterator<BulletinBoardMessage> receivedMessageIterator = messages.iterator();
|
||||
|
||||
while (expectedMessageIterator.hasNext()) {
|
||||
assertThat(msgComparator.compare(expectedMessageIterator.next(), receivedMessageIterator.next()), is(0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
genericHandleFailure(t);
|
||||
}
|
||||
}
|
||||
|
||||
private class ReadBatchCallback implements FutureCallback<CompleteBatch> {
|
||||
|
||||
private CompleteBatch expectedBatch;
|
||||
|
||||
public ReadBatchCallback(CompleteBatch expectedBatch) {
|
||||
this.expectedBatch = expectedBatch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(CompleteBatch batch) {
|
||||
|
||||
System.err.println(batch);
|
||||
jobSemaphore.release();
|
||||
|
||||
assertThat("Batch returned is incorrect", batch, is(equalTo(expectedBatch)));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
genericHandleFailure(t);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
/**
|
||||
* Takes care of initializing the client and the test resources
|
||||
*/
|
||||
public void init(){
|
||||
|
||||
random = new Random(0); // We use insecure randomness in tests for repeatability
|
||||
|
||||
postCallback = new PostCallback();
|
||||
redundancyCallback = new RedundancyCallback((float) 1.0);
|
||||
|
||||
thrown = new Vector<>();
|
||||
jobSemaphore = new Semaphore(0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the client and makes sure the test fails when an exception occurred in a separate thread
|
||||
*/
|
||||
|
||||
public void close() {
|
||||
|
||||
if (thrown.size() > 0) {
|
||||
assert false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the standard post, redundancy and read methods
|
||||
*/
|
||||
public void postTest() {
|
||||
|
||||
byte[] b1 = {(byte) 1, (byte) 2, (byte) 3, (byte) 4};
|
||||
byte[] b2 = {(byte) 11, (byte) 12, (byte) 13, (byte) 14};
|
||||
byte[] b3 = {(byte) 21, (byte) 22, (byte) 23, (byte) 24};
|
||||
|
||||
BulletinBoardMessage msg;
|
||||
|
||||
MessageFilterList filterList;
|
||||
List<BulletinBoardMessage> msgList;
|
||||
|
||||
MessageID messageID;
|
||||
|
||||
msg = BulletinBoardMessage.newBuilder()
|
||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||
.addTag("Signature")
|
||||
.addTag("Trustee")
|
||||
.setData(ByteString.copyFrom(b1))
|
||||
.setTimestamp(Timestamp.newBuilder()
|
||||
.setSeconds(20)
|
||||
.setNanos(30)
|
||||
.build())
|
||||
.build())
|
||||
.addSig(Crypto.Signature.newBuilder()
|
||||
.setType(Crypto.SignatureType.DSA)
|
||||
.setData(ByteString.copyFrom(b2))
|
||||
.setSignerId(ByteString.copyFrom(b3))
|
||||
.build())
|
||||
.addSig(Crypto.Signature.newBuilder()
|
||||
.setType(Crypto.SignatureType.ECDSA)
|
||||
.setData(ByteString.copyFrom(b3))
|
||||
.setSignerId(ByteString.copyFrom(b2))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
messageID = bulletinBoardClient.postMessage(msg,postCallback);
|
||||
|
||||
try {
|
||||
jobSemaphore.acquire();
|
||||
} catch (InterruptedException e) {
|
||||
System.err.println(e.getCause() + " " + e.getMessage());
|
||||
}
|
||||
|
||||
bulletinBoardClient.getRedundancy(messageID,redundancyCallback);
|
||||
|
||||
filterList = MessageFilterList.newBuilder()
|
||||
.addFilter(
|
||||
MessageFilter.newBuilder()
|
||||
.setType(FilterType.TAG)
|
||||
.setTag("Signature")
|
||||
.build()
|
||||
)
|
||||
.addFilter(
|
||||
MessageFilter.newBuilder()
|
||||
.setType(FilterType.TAG)
|
||||
.setTag("Trustee")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
msgList = new LinkedList<>();
|
||||
msgList.add(msg);
|
||||
|
||||
readCallback = new ReadCallback(msgList);
|
||||
|
||||
bulletinBoardClient.readMessages(filterList, readCallback);
|
||||
try {
|
||||
jobSemaphore.acquire(2);
|
||||
} catch (InterruptedException e) {
|
||||
System.err.println(e.getCause() + " " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests posting a batch by parts
|
||||
* Also tests not being able to post to a closed batch
|
||||
* @throws CommunicationException, SignatureException, InterruptedException
|
||||
*/
|
||||
public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException {
|
||||
|
||||
final int SIGNER = 1;
|
||||
final int BATCH_ID = 100;
|
||||
final int BATCH_LENGTH = 100;
|
||||
|
||||
CompleteBatch completeBatch = createRandomBatch(SIGNER, BATCH_ID, BATCH_LENGTH);
|
||||
|
||||
// Begin batch
|
||||
|
||||
bulletinBoardClient.beginBatch(completeBatch.getBeginBatchMessage(), postCallback);
|
||||
|
||||
jobSemaphore.acquire();
|
||||
|
||||
// Post data
|
||||
|
||||
bulletinBoardClient.postBatchData(signerIDs[SIGNER], BATCH_ID, completeBatch.getBatchDataList(), postCallback);
|
||||
|
||||
jobSemaphore.acquire();
|
||||
|
||||
// Close batch
|
||||
|
||||
CloseBatchMessage closeBatchMessage = completeBatch.getCloseBatchMessage();
|
||||
|
||||
bulletinBoardClient.closeBatch(closeBatchMessage, postCallback);
|
||||
|
||||
jobSemaphore.acquire();
|
||||
|
||||
// Attempt to open batch again
|
||||
|
||||
bulletinBoardClient.beginBatch(completeBatch.getBeginBatchMessage(), failPostCallback);
|
||||
|
||||
// Attempt to add batch data
|
||||
|
||||
bulletinBoardClient.postBatchData(signerIDs[SIGNER], BATCH_ID, completeBatch.getBatchDataList(), failPostCallback);
|
||||
|
||||
jobSemaphore.acquire(2);
|
||||
|
||||
// Read batch data
|
||||
|
||||
BatchSpecificationMessage batchSpecificationMessage =
|
||||
BatchSpecificationMessage.newBuilder()
|
||||
.setSignerId(signerIDs[SIGNER])
|
||||
.setBatchId(BATCH_ID)
|
||||
.setStartPosition(0)
|
||||
.build();
|
||||
|
||||
readBatchCallback = new ReadBatchCallback(completeBatch);
|
||||
|
||||
bulletinBoardClient.readBatch(batchSpecificationMessage, readBatchCallback);
|
||||
|
||||
jobSemaphore.acquire();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts a complete batch message
|
||||
* Checks reading of the message
|
||||
* @throws CommunicationException, SignatureException, InterruptedException
|
||||
*/
|
||||
public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException {
|
||||
|
||||
final int SIGNER = 0;
|
||||
final int BATCH_ID = 101;
|
||||
final int BATCH_LENGTH = 50;
|
||||
|
||||
// Post batch
|
||||
|
||||
CompleteBatch completeBatch = createRandomBatch(SIGNER, BATCH_ID, BATCH_LENGTH);
|
||||
|
||||
bulletinBoardClient.postBatch(completeBatch,postCallback);
|
||||
|
||||
jobSemaphore.acquire();
|
||||
|
||||
// Read batch
|
||||
|
||||
BatchSpecificationMessage batchSpecificationMessage =
|
||||
BatchSpecificationMessage.newBuilder()
|
||||
.setSignerId(signerIDs[SIGNER])
|
||||
.setBatchId(BATCH_ID)
|
||||
.setStartPosition(0)
|
||||
.build();
|
||||
|
||||
readBatchCallback = new ReadBatchCallback(completeBatch);
|
||||
|
||||
bulletinBoardClient.readBatch(batchSpecificationMessage, readBatchCallback);
|
||||
|
||||
jobSemaphore.acquire();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an unopened batch cannot be closed
|
||||
* @throws CommunicationException, InterruptedException
|
||||
*/
|
||||
public void testInvalidBatchClose() throws CommunicationException, InterruptedException {
|
||||
|
||||
final int NON_EXISTENT_BATCH_ID = 999;
|
||||
|
||||
CloseBatchMessage closeBatchMessage =
|
||||
CloseBatchMessage.newBuilder()
|
||||
.setBatchId(NON_EXISTENT_BATCH_ID)
|
||||
.setBatchLength(1)
|
||||
.setSig(Crypto.Signature.getDefaultInstance())
|
||||
.setTimestamp(Timestamp.newBuilder()
|
||||
.setSeconds(9)
|
||||
.setNanos(12)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
// Try to stop the (unopened) batch;
|
||||
|
||||
bulletinBoardClient.closeBatch(closeBatchMessage, failPostCallback);
|
||||
|
||||
jobSemaphore.acquire();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,231 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Timestamp;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.crypto.concrete.ECDSASignature;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.util.BulletinBoardMessageComparator;
|
||||
import meerkat.util.BulletinBoardMessageGenerator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.startsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 22-Mar-16.
|
||||
*/
|
||||
public class GenericSubscriptionClientTester {
|
||||
|
||||
private GenericBatchDigitalSignature signers[];
|
||||
private ByteString[] signerIDs;
|
||||
|
||||
private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
|
||||
private static String KEYFILE_EXAMPLE3 = "/certs/enduser-certs/user3-key-with-password-shh.p12";
|
||||
|
||||
private static String KEYFILE_PASSWORD1 = "secret";
|
||||
private static String KEYFILE_PASSWORD3 = "shh";
|
||||
|
||||
private static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt";
|
||||
private static String CERT3_PEM_EXAMPLE = "/certs/enduser-certs/user3.crt";
|
||||
|
||||
private SubscriptionAsyncBulletinBoardClient bulletinBoardClient;
|
||||
|
||||
private Random random;
|
||||
private BulletinBoardMessageGenerator generator;
|
||||
|
||||
private Semaphore jobSemaphore;
|
||||
private Vector<Throwable> thrown;
|
||||
|
||||
public GenericSubscriptionClientTester(SubscriptionAsyncBulletinBoardClient bulletinBoardClient){
|
||||
|
||||
this.bulletinBoardClient = bulletinBoardClient;
|
||||
|
||||
signers = new GenericBatchDigitalSignature[2];
|
||||
signerIDs = new ByteString[signers.length];
|
||||
signers[0] = new GenericBatchDigitalSignature(new ECDSASignature());
|
||||
signers[1] = new GenericBatchDigitalSignature(new ECDSASignature());
|
||||
|
||||
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
||||
char[] password = KEYFILE_PASSWORD1.toCharArray();
|
||||
|
||||
KeyStore.Builder keyStoreBuilder;
|
||||
try {
|
||||
keyStoreBuilder = signers[0].getPKCS12KeyStoreBuilder(keyStream, password);
|
||||
|
||||
signers[0].loadSigningCertificate(keyStoreBuilder);
|
||||
|
||||
signers[0].loadVerificationCertificates(getClass().getResourceAsStream(CERT1_PEM_EXAMPLE));
|
||||
|
||||
keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE3);
|
||||
password = KEYFILE_PASSWORD3.toCharArray();
|
||||
|
||||
keyStoreBuilder = signers[1].getPKCS12KeyStoreBuilder(keyStream, password);
|
||||
signers[1].loadSigningCertificate(keyStoreBuilder);
|
||||
|
||||
signers[1].loadVerificationCertificates(getClass().getResourceAsStream(CERT3_PEM_EXAMPLE));
|
||||
|
||||
for (int i = 0 ; i < signers.length ; i++) {
|
||||
signerIDs[i] = signers[i].getSignerID();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed reading from signature file " + e.getMessage());
|
||||
fail("Failed reading from signature file " + e.getMessage());
|
||||
} catch (CertificateException e) {
|
||||
System.err.println("Failed reading certificate " + e.getMessage());
|
||||
fail("Failed reading certificate " + e.getMessage());
|
||||
} catch (KeyStoreException e) {
|
||||
System.err.println("Failed reading keystore " + e.getMessage());
|
||||
fail("Failed reading keystore " + e.getMessage());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
System.err.println("Couldn't find signing algorithm " + e.getMessage());
|
||||
fail("Couldn't find signing algorithm " + e.getMessage());
|
||||
} catch (UnrecoverableKeyException e) {
|
||||
System.err.println("Couldn't find signing key " + e.getMessage());
|
||||
fail("Couldn't find signing key " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes care of initializing the client and the test resources
|
||||
*/
|
||||
public void init(){
|
||||
|
||||
random = new Random(0); // We use insecure randomness in tests for repeatability
|
||||
generator = new BulletinBoardMessageGenerator(random);
|
||||
|
||||
thrown = new Vector<>();
|
||||
jobSemaphore = new Semaphore(0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the client and makes sure the test fails when an exception occurred in a separate thread
|
||||
*/
|
||||
|
||||
public void close() {
|
||||
|
||||
if (thrown.size() > 0) {
|
||||
assert false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class SubscriptionCallback implements FutureCallback<List<BulletinBoardMessage>>{
|
||||
|
||||
private int stage;
|
||||
private final List<List<BulletinBoardMessage>> expectedMessages;
|
||||
private final List<BulletinBoardMessage> messagesToPost;
|
||||
private final BulletinBoardMessageComparator comparator;
|
||||
|
||||
public SubscriptionCallback(List<List<BulletinBoardMessage>> expectedMessages, List<BulletinBoardMessage> messagesToPost) {
|
||||
|
||||
this.expectedMessages = expectedMessages;
|
||||
this.messagesToPost = messagesToPost;
|
||||
this.stage = 0;
|
||||
this.comparator = new BulletinBoardMessageComparator();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(List<BulletinBoardMessage> result) {
|
||||
|
||||
if (stage >= expectedMessages.size())
|
||||
return;
|
||||
|
||||
// Check for consistency
|
||||
|
||||
List<BulletinBoardMessage> expectedMsgList = expectedMessages.get(stage);
|
||||
|
||||
if (expectedMsgList.size() != result.size()){
|
||||
onFailure(new AssertionError("Received wrong number of messages"));
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator<BulletinBoardMessage> expectedMessageIterator = expectedMsgList.iterator();
|
||||
Iterator<BulletinBoardMessage> receivedMessageIterator = result.iterator();
|
||||
|
||||
while (expectedMessageIterator.hasNext()) {
|
||||
if(comparator.compare(expectedMessageIterator.next(), receivedMessageIterator.next()) != 0){
|
||||
onFailure(new AssertionError("Received unexpected message"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Post new message
|
||||
try {
|
||||
if (stage < messagesToPost.size()) {
|
||||
bulletinBoardClient.postMessage(messagesToPost.get(stage));
|
||||
}
|
||||
} catch (CommunicationException e) {
|
||||
onFailure(e);
|
||||
return;
|
||||
}
|
||||
|
||||
stage++;
|
||||
jobSemaphore.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
System.err.println(t.getCause() + " " + t.getMessage());
|
||||
thrown.add(t);
|
||||
jobSemaphore.release(expectedMessages.size());
|
||||
stage = expectedMessages.size();
|
||||
}
|
||||
}
|
||||
|
||||
public void subscriptionTest() throws SignatureException, CommunicationException {
|
||||
|
||||
final int FIRST_POST_ID = 201;
|
||||
final int SECOND_POST_ID = 202;
|
||||
final String COMMON_TAG = "SUBSCRIPTION_TEST";
|
||||
|
||||
List<String> tags = new LinkedList<>();
|
||||
tags.add(COMMON_TAG);
|
||||
|
||||
BulletinBoardMessage msg1 = generator.generateRandomMessage(signers, Timestamp.newBuilder().setSeconds(1000).setNanos(900).build(), 10, 4, tags);
|
||||
BulletinBoardMessage msg2 = generator.generateRandomMessage(signers, Timestamp.newBuilder().setSeconds(800).setNanos(300).build(), 10, 4);
|
||||
BulletinBoardMessage msg3 = generator.generateRandomMessage(signers, Timestamp.newBuilder().setSeconds(2000).setNanos(0).build(), 10, 4, tags);
|
||||
|
||||
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||
.addFilter(MessageFilter.newBuilder()
|
||||
.setType(FilterType.TAG)
|
||||
.setTag(COMMON_TAG)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
List<List<BulletinBoardMessage>> expectedMessages = new ArrayList<>(3);
|
||||
expectedMessages.add(new LinkedList<BulletinBoardMessage>());
|
||||
expectedMessages.add(new LinkedList<BulletinBoardMessage>());
|
||||
expectedMessages.add(new LinkedList<BulletinBoardMessage>());
|
||||
expectedMessages.get(0).add(msg1);
|
||||
expectedMessages.get(2).add(msg3);
|
||||
|
||||
List<BulletinBoardMessage> messagesToPost = new ArrayList<>(2);
|
||||
messagesToPost.add(msg2);
|
||||
messagesToPost.add(msg3);
|
||||
|
||||
bulletinBoardClient.postMessage(msg1);
|
||||
bulletinBoardClient.subscribe(filterList, new SubscriptionCallback(expectedMessages, messagesToPost));
|
||||
|
||||
try {
|
||||
jobSemaphore.acquire(3);
|
||||
} catch (InterruptedException e) {
|
||||
System.err.println(e.getCause() + " " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import meerkat.bulletinboard.sqlserver.*;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.security.SignatureException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 05-Dec-15.
|
||||
*/
|
||||
public class LocalBulletinBoardClientTest {
|
||||
|
||||
private static final int THREAD_NUM = 3;
|
||||
private static final String DB_NAME = "TestDB";
|
||||
|
||||
private static final int SUBSRCIPTION_DELAY = 3000;
|
||||
|
||||
// Testers
|
||||
private GenericBulletinBoardClientTester clientTest;
|
||||
private GenericSubscriptionClientTester subscriptionTester;
|
||||
|
||||
public LocalBulletinBoardClientTest() throws CommunicationException {
|
||||
|
||||
H2QueryProvider queryProvider = new H2QueryProvider(DB_NAME) ;
|
||||
|
||||
try {
|
||||
|
||||
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);
|
||||
subscriptionTester = new GenericSubscriptionClientTester(client);
|
||||
clientTest = new GenericBulletinBoardClientTester(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 postTest() {
|
||||
|
||||
clientTest.postTest();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException {
|
||||
|
||||
clientTest.testBatchPost();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException {
|
||||
|
||||
clientTest.testCompleteBatchPost();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidBatchClose() throws CommunicationException, InterruptedException {
|
||||
|
||||
clientTest.testInvalidBatchClose();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscription() throws SignatureException, CommunicationException {
|
||||
subscriptionTester.init();
|
||||
subscriptionTester.subscriptionTest();
|
||||
subscriptionTester.close();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import meerkat.comm.CommunicationException;
|
||||
|
||||
import meerkat.protobuf.Voting.*;
|
||||
|
||||
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 Deutsch Peled on 05-Dec-15.
|
||||
*/
|
||||
public class ThreadedBulletinBoardClientIntegrationTest {
|
||||
|
||||
// Server data
|
||||
|
||||
private static String PROP_GETTY_URL = "gretty.httpBaseURI";
|
||||
private static String DEFAULT_BASE_URL = "http://localhost:8081";
|
||||
private static String BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL);
|
||||
|
||||
// Tester
|
||||
private GenericBulletinBoardClientTester clientTest;
|
||||
|
||||
public ThreadedBulletinBoardClientIntegrationTest(){
|
||||
|
||||
ThreadedBulletinBoardClient client = new ThreadedBulletinBoardClient();
|
||||
|
||||
List<String> testDB = new LinkedList<>();
|
||||
testDB.add(BASE_URL);
|
||||
|
||||
client.init(BulletinBoardClientParams.newBuilder()
|
||||
.addAllBulletinBoardAddress(testDB)
|
||||
.setMinRedundancy((float) 1.0)
|
||||
.build());
|
||||
|
||||
clientTest = new GenericBulletinBoardClientTester(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 postTest() {
|
||||
|
||||
clientTest.postTest();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException {
|
||||
|
||||
clientTest.testBatchPost();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException {
|
||||
|
||||
clientTest.testCompleteBatchPost();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidBatchClose() throws CommunicationException, InterruptedException {
|
||||
|
||||
clientTest.testInvalidBatchClose();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1 +1 @@
|
|||
/bin/
|
||||
/bin/
|
||||
|
|
|
@ -48,9 +48,10 @@ dependencies {
|
|||
|
||||
// JDBC connections
|
||||
compile 'org.springframework:spring-jdbc:4.2.+'
|
||||
compile 'org.xerial:sqlite-jdbc:3.7.+'
|
||||
compile 'org.xerial:sqlite-jdbc:3.8.+'
|
||||
compile 'mysql:mysql-connector-java:5.1.+'
|
||||
compile 'com.h2database:h2:1.0.+'
|
||||
compile 'org.apache.commons:commons-dbcp2:2.0.+'
|
||||
|
||||
// Servlets
|
||||
compile 'javax.servlet:javax.servlet-api:3.0.+'
|
||||
|
@ -75,13 +76,34 @@ dependencies {
|
|||
test {
|
||||
exclude '**/*SQLite*Test*'
|
||||
exclude '**/*H2*Test*'
|
||||
exclude '**/*MySql*Test'
|
||||
exclude '**/*MySQL*Test*'
|
||||
exclude '**/*IntegrationTest*'
|
||||
}
|
||||
|
||||
task myTest(type: Test) {
|
||||
include '**/*MySQL*Test*'
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
|
||||
task h2Test(type: Test) {
|
||||
include '**/*H2*Test*'
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
|
||||
task liteTest(type: Test) {
|
||||
include '**/*SQLite*Test*'
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
|
||||
task dbTest(type: Test) {
|
||||
include '**/*H2*Test*'
|
||||
include '**/*MySql*Test'
|
||||
include '**/*MySQL*Test*'
|
||||
include '**/*SQLite*Test*'
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
|
||||
task manualIntegration(type: Test) {
|
||||
include '**/*IntegrationTest*'
|
||||
}
|
||||
|
||||
task integrationTest(type: Test) {
|
||||
|
@ -238,4 +260,3 @@ publishing {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<root>
|
||||
<setting name="task-tab">
|
||||
<setting name="show-description" value="true"/>
|
||||
</setting>
|
||||
<setting name="favorites-tab"/>
|
||||
<setting name="command_line-tab"/>
|
||||
<setting name="setup-tab">
|
||||
<setting name="setup">
|
||||
<setting name="custom-gradle-executor"/>
|
||||
<setting name="current-directory" value="/home/talm/proj/meerkat/bulletin-board-server"/>
|
||||
<setting name="log-level" value="LIFECYCLE"/>
|
||||
</setting>
|
||||
</setting>
|
||||
<setting name="SinglePaneUIInstance_splitter-id">
|
||||
<setting name="divider_location" value="387"/>
|
||||
</setting>
|
||||
<setting name="main_panel">
|
||||
<setting name="current-tab" value="Task Tree"/>
|
||||
</setting>
|
||||
<setting name="Application_window-id">
|
||||
<setting name="window_x" value="306"/>
|
||||
<setting name="window_y" value="1042"/>
|
||||
<setting name="window_width" value="800"/>
|
||||
<setting name="window_height" value="800"/>
|
||||
<setting name="extended-state" value="0"/>
|
||||
</setting>
|
||||
</root>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<root>
|
||||
<setting name="task-tab">
|
||||
<setting name="show-description" value="true"/>
|
||||
</setting>
|
||||
<setting name="favorites-tab"/>
|
||||
<setting name="command_line-tab"/>
|
||||
<setting name="setup-tab">
|
||||
<setting name="setup">
|
||||
<setting name="custom-gradle-executor"/>
|
||||
<setting name="current-directory" value="/home/talm/proj/meerkat/bulletin-board-server"/>
|
||||
<setting name="log-level" value="LIFECYCLE"/>
|
||||
</setting>
|
||||
</setting>
|
||||
<setting name="SinglePaneUIInstance_splitter-id">
|
||||
<setting name="divider_location" value="387"/>
|
||||
</setting>
|
||||
<setting name="main_panel">
|
||||
<setting name="current-tab" value="Task Tree"/>
|
||||
</setting>
|
||||
<setting name="Application_window-id">
|
||||
<setting name="window_x" value="306"/>
|
||||
<setting name="window_y" value="1042"/>
|
||||
<setting name="window_width" value="800"/>
|
||||
<setting name="window_height" value="800"/>
|
||||
<setting name="extended-state" value="0"/>
|
||||
</setting>
|
||||
</root>
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
package meerkat.bulletinboard.service;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Message;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by talm on 10/11/15.
|
||||
*/
|
||||
public class HelloProtoBuf {
|
||||
public Message sayHello() {
|
||||
BulletinBoardMessage.Builder msg = BulletinBoardMessage.newBuilder();
|
||||
|
||||
UnsignedBulletinBoardMessage.Builder unsigned = UnsignedBulletinBoardMessage.newBuilder();
|
||||
unsigned.setData(ByteString.copyFromUtf8("Hello World!"));
|
||||
List<String> tags = Arrays.asList("Greetings", "FirstPrograms");
|
||||
unsigned.addAllTag(tags);
|
||||
msg.setMsg(unsigned);
|
||||
|
||||
Crypto.Signature.Builder sig = Crypto.Signature.newBuilder();
|
||||
sig.setData(ByteString.copyFromUtf8("deadbeef"));
|
||||
msg.addSig(sig);
|
||||
|
||||
return msg.build();
|
||||
}
|
||||
}
|
||||
package meerkat.bulletinboard.service;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Message;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by talm on 10/11/15.
|
||||
*/
|
||||
public class HelloProtoBuf {
|
||||
public Message sayHello() {
|
||||
BulletinBoardMessage.Builder msg = BulletinBoardMessage.newBuilder();
|
||||
|
||||
UnsignedBulletinBoardMessage.Builder unsigned = UnsignedBulletinBoardMessage.newBuilder();
|
||||
unsigned.setData(ByteString.copyFromUtf8("Hello World!"));
|
||||
List<String> tags = Arrays.asList("Greetings", "FirstPrograms");
|
||||
unsigned.addAllTag(tags);
|
||||
msg.setMsg(unsigned);
|
||||
|
||||
Crypto.Signature.Builder sig = Crypto.Signature.newBuilder();
|
||||
sig.setData(ByteString.copyFromUtf8("deadbeef"));
|
||||
msg.addSig(sig);
|
||||
|
||||
return msg.build();
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,164 +1,266 @@
|
|||
package meerkat.bulletinboard.sqlserver;
|
||||
|
||||
import meerkat.protobuf.BulletinBoardAPI.FilterType;
|
||||
import org.h2.jdbcx.JdbcDataSource;
|
||||
import javax.naming.Context;
|
||||
import javax.naming.InitialContext;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.sql.DataSource;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||
*/
|
||||
|
||||
public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider {
|
||||
|
||||
private String dbName;
|
||||
|
||||
public H2QueryProvider(String dbName) {
|
||||
this.dbName = dbName;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getSQLString(QueryType queryType) throws IllegalArgumentException{
|
||||
|
||||
switch(queryType) {
|
||||
case ADD_SIGNATURE:
|
||||
return "INSERT INTO SignatureTable (EntryNum, SignerId, Signature)"
|
||||
+ " SELECT DISTINCT :EntryNum AS Entry, :SignerId AS Id, :Signature AS Sig FROM UtilityTable AS Temp"
|
||||
+ " WHERE NOT EXISTS"
|
||||
+ " (SELECT 1 FROM SignatureTable AS SubTable WHERE SubTable.SignerId = :SignerId AND SubTable.EntryNum = :EntryNum)";
|
||||
|
||||
case CONNECT_TAG:
|
||||
return "INSERT INTO MsgTagTable (TagId, EntryNum)"
|
||||
+ " SELECT DISTINCT TagTable.TagId, :EntryNum AS NewEntry FROM TagTable WHERE Tag = :Tag"
|
||||
+ " AND NOT EXISTS (SELECT 1 FROM MsgTagTable AS SubTable WHERE SubTable.TagId = TagTable.TagId"
|
||||
+ " AND SubTable.EntryNum = :EntryNum)";
|
||||
|
||||
case FIND_MSG_ID:
|
||||
return "SELECT EntryNum From MsgTable WHERE MsgId = :MsgId";
|
||||
|
||||
case GET_MESSAGES:
|
||||
return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
|
||||
|
||||
case GET_SIGNATURES:
|
||||
return "SELECT Signature FROM SignatureTable WHERE EntryNum = :EntryNum";
|
||||
|
||||
case INSERT_MSG:
|
||||
return "INSERT INTO MsgTable (MsgId, Msg) VALUES(:MsgId,:Msg)";
|
||||
|
||||
case INSERT_NEW_TAG:
|
||||
return "INSERT INTO TagTable(Tag) SELECT DISTINCT :Tag AS NewTag FROM UtilityTable WHERE"
|
||||
+ " NOT EXISTS (SELECT 1 FROM TagTable AS SubTable WHERE SubTable.Tag = :Tag)";
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException {
|
||||
|
||||
String serialString = Integer.toString(serialNum);
|
||||
|
||||
switch(filterType) {
|
||||
case EXACT_ENTRY:
|
||||
return "MsgTable.EntryNum = :EntryNum" + serialString;
|
||||
case MAX_ENTRY:
|
||||
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
||||
case MAX_MESSAGES:
|
||||
return "LIMIT :Limit" + serialString;
|
||||
case MSG_ID:
|
||||
return "MsgTable.MsgId = MsgId" + serialString;
|
||||
case SIGNER_ID:
|
||||
return "EXISTS (SELECT 1 FROM SignatureTable"
|
||||
+ " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)";
|
||||
case TAG:
|
||||
return "EXISTS (SELECT 1 FROM TagTable"
|
||||
+ " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId"
|
||||
+ " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)";
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException {
|
||||
|
||||
switch(filterType) {
|
||||
case EXACT_ENTRY: // Go through
|
||||
case MAX_ENTRY: // Go through
|
||||
case MAX_MESSAGES:
|
||||
return "INT";
|
||||
|
||||
case MSG_ID: // Go through
|
||||
case SIGNER_ID:
|
||||
return "TINYBLOB";
|
||||
|
||||
case TAG:
|
||||
return "VARCHAR";
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource getDataSource() {
|
||||
|
||||
JdbcDataSource dataSource = new JdbcDataSource();
|
||||
dataSource.setURL("jdbc:h2:~/" + dbName);
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
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 UNIQUE, Msg BLOB)");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INT NOT NULL AUTO_INCREMENT PRIMARY KEY, Tag VARCHAR(50) UNIQUE)");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum INT, TagId INT,"
|
||||
+ " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum),"
|
||||
+ " FOREIGN KEY (TagId) REFERENCES TagTable(TagId),"
|
||||
+ " UNIQUE (EntryNum, TagID))");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INT, SignerId TINYBLOB, Signature TINYBLOB UNIQUE,"
|
||||
+ " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))");
|
||||
|
||||
list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)");
|
||||
list.add("CREATE UNIQUE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId, EntryNum)");
|
||||
|
||||
// This is used to create a simple table with one entry.
|
||||
// It is used for implementing a workaround for the missing INSERT IGNORE syntax
|
||||
list.add("CREATE TABLE IF NOT EXISTS UtilityTable (Entry INT)");
|
||||
list.add("INSERT INTO UtilityTable (Entry) VALUES (1)");
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSchemaDeletionCommands() {
|
||||
List<String> list = new LinkedList<String>();
|
||||
|
||||
list.add("DROP TABLE IF EXISTS UtilityTable");
|
||||
list.add("DROP INDEX IF EXISTS SignerIdIndex");
|
||||
list.add("DROP TABLE IF EXISTS MsgTagTable");
|
||||
list.add("DROP TABLE IF EXISTS SignatureTable");
|
||||
list.add("DROP TABLE IF EXISTS TagTable");
|
||||
list.add("DROP TABLE IF EXISTS MsgTable");
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
package meerkat.bulletinboard.sqlserver;
|
||||
|
||||
import meerkat.protobuf.BulletinBoardAPI.FilterType;
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
import org.h2.jdbcx.JdbcDataSource;
|
||||
import javax.naming.Context;
|
||||
import javax.naming.InitialContext;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.sql.DataSource;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||
*/
|
||||
|
||||
public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider {
|
||||
|
||||
private String dbName;
|
||||
|
||||
public H2QueryProvider(String dbName) {
|
||||
this.dbName = dbName;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getSQLString(QueryType queryType) throws IllegalArgumentException{
|
||||
|
||||
switch(queryType) {
|
||||
case ADD_SIGNATURE:
|
||||
return "INSERT INTO SignatureTable (EntryNum, SignerId, Signature)"
|
||||
+ " SELECT DISTINCT :EntryNum AS Entry, :SignerId AS Id, :Signature AS Sig FROM UtilityTable AS Temp"
|
||||
+ " WHERE NOT EXISTS"
|
||||
+ " (SELECT 1 FROM SignatureTable AS SubTable WHERE SubTable.SignerId = :SignerId AND SubTable.EntryNum = :EntryNum)";
|
||||
|
||||
case CONNECT_TAG:
|
||||
return "INSERT INTO MsgTagTable (TagId, EntryNum)"
|
||||
+ " SELECT DISTINCT TagTable.TagId, :EntryNum AS NewEntry FROM TagTable WHERE Tag = :Tag"
|
||||
+ " AND NOT EXISTS (SELECT 1 FROM MsgTagTable AS SubTable WHERE SubTable.TagId = TagTable.TagId"
|
||||
+ " AND SubTable.EntryNum = :EntryNum)";
|
||||
|
||||
case FIND_MSG_ID:
|
||||
return "SELECT EntryNum From MsgTable WHERE MsgId = :MsgId";
|
||||
|
||||
case FIND_TAG_ID:
|
||||
return MessageFormat.format(
|
||||
"SELECT TagId FROM TagTable WHERE Tag = :{0}",
|
||||
QueryType.FIND_TAG_ID.getParamName(0));
|
||||
|
||||
case GET_MESSAGES:
|
||||
return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
|
||||
|
||||
case COUNT_MESSAGES:
|
||||
return "SELECT COUNT(MsgTable.EntryNum) FROM MsgTable";
|
||||
|
||||
case GET_MESSAGE_STUBS:
|
||||
return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable";
|
||||
|
||||
case GET_SIGNATURES:
|
||||
return "SELECT Signature FROM SignatureTable WHERE EntryNum = :EntryNum";
|
||||
|
||||
case INSERT_MSG:
|
||||
return "INSERT INTO MsgTable (MsgId, Msg, ExactTime) VALUES(:MsgId,:Msg,:TimeStamp)";
|
||||
|
||||
case INSERT_NEW_TAG:
|
||||
return "INSERT INTO TagTable(Tag) SELECT DISTINCT :Tag AS NewTag FROM UtilityTable WHERE"
|
||||
+ " NOT EXISTS (SELECT 1 FROM TagTable AS SubTable WHERE SubTable.Tag = :Tag)";
|
||||
|
||||
case GET_LAST_MESSAGE_ENTRY:
|
||||
return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable";
|
||||
|
||||
case GET_BATCH_MESSAGE_ENTRY:
|
||||
return MessageFormat.format(
|
||||
"SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"
|
||||
+ " INNER JOIN SignatureTable ON MsgTable.EntryNum = SignatureTable.EntryNum"
|
||||
+ " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum"
|
||||
+ " INNER JOIN TagTable ON MsgTagTable.TagId = TagTable.TagId"
|
||||
+ " WHERE SignatureTable.SignerId = :{0}"
|
||||
+ " AND TagTable.Tag = :{1}",
|
||||
QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0),
|
||||
QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1));
|
||||
|
||||
case GET_BATCH_MESSAGE_DATA:
|
||||
return MessageFormat.format(
|
||||
"SELECT Data FROM BatchTable"
|
||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}"
|
||||
+ " ORDER BY SerialNum ASC",
|
||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
|
||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),
|
||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2));
|
||||
|
||||
case INSERT_BATCH_DATA:
|
||||
return MessageFormat.format(
|
||||
"INSERT INTO BatchTable (SignerId, BatchId, SerialNum, Data)"
|
||||
+ " VALUES (:{0}, :{1}, :{2}, :{3})",
|
||||
QueryType.INSERT_BATCH_DATA.getParamName(0),
|
||||
QueryType.INSERT_BATCH_DATA.getParamName(1),
|
||||
QueryType.INSERT_BATCH_DATA.getParamName(2),
|
||||
QueryType.INSERT_BATCH_DATA.getParamName(3));
|
||||
|
||||
case CHECK_BATCH_LENGTH:
|
||||
return MessageFormat.format(
|
||||
"SELECT COUNT(Data) AS BatchLength FROM BatchTable"
|
||||
+ " WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||
QueryType.CHECK_BATCH_LENGTH.getParamName(0),
|
||||
QueryType.CHECK_BATCH_LENGTH.getParamName(1));
|
||||
|
||||
case CONNECT_BATCH_TAG:
|
||||
return MessageFormat.format(
|
||||
"INSERT INTO BatchTagTable (SignerId, BatchId, TagId) SELECT :{0}, :{1}, TagId FROM TagTable"
|
||||
+ " WHERE Tag = :{2}",
|
||||
QueryType.CONNECT_BATCH_TAG.getParamName(0),
|
||||
QueryType.CONNECT_BATCH_TAG.getParamName(1),
|
||||
QueryType.CONNECT_BATCH_TAG.getParamName(2));
|
||||
|
||||
case GET_BATCH_TAGS:
|
||||
return MessageFormat.format(
|
||||
"SELECT Tag FROM TagTable INNER JOIN BatchTagTable ON TagTable.TagId = BatchTagTable.TagId"
|
||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} ORDER BY Tag ASC",
|
||||
QueryType.GET_BATCH_TAGS.getParamName(0),
|
||||
QueryType.GET_BATCH_TAGS.getParamName(1));
|
||||
|
||||
case REMOVE_BATCH_TAGS:
|
||||
return MessageFormat.format(
|
||||
"DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||
QueryType.REMOVE_BATCH_TAGS.getParamName(0),
|
||||
QueryType.REMOVE_BATCH_TAGS.getParamName(1));
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException {
|
||||
|
||||
String serialString = Integer.toString(serialNum);
|
||||
|
||||
switch(filterType) {
|
||||
case EXACT_ENTRY:
|
||||
return "MsgTable.EntryNum = :EntryNum" + serialString;
|
||||
case MAX_ENTRY:
|
||||
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
||||
case MIN_ENTRY:
|
||||
return "MsgTable.EntryNum >= :EntryNum" + serialString;
|
||||
case MAX_MESSAGES:
|
||||
return "LIMIT :Limit" + serialString;
|
||||
case MSG_ID:
|
||||
return "MsgTable.MsgId = :MsgId" + serialString;
|
||||
case SIGNER_ID:
|
||||
return "EXISTS (SELECT 1 FROM SignatureTable"
|
||||
+ " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)";
|
||||
case TAG:
|
||||
return "EXISTS (SELECT 1 FROM TagTable"
|
||||
+ " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId"
|
||||
+ " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)";
|
||||
|
||||
case BEFORE_TIME:
|
||||
return "MsgTable.ExactTime <= :TimeStamp" + serialString;
|
||||
|
||||
case AFTER_TIME:
|
||||
return "MsgTable.ExactTime >= :TimeStamp" + serialString;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException {
|
||||
|
||||
switch(filterType) {
|
||||
case EXACT_ENTRY: // Go through
|
||||
case MAX_ENTRY: // Go through
|
||||
case MIN_ENTRY: // Go through
|
||||
case MAX_MESSAGES:
|
||||
return "INT";
|
||||
|
||||
case MSG_ID: // Go through
|
||||
case SIGNER_ID:
|
||||
return "TINYBLOB";
|
||||
|
||||
case TAG:
|
||||
return "VARCHAR";
|
||||
|
||||
case AFTER_TIME: // Go through
|
||||
case BEFORE_TIME:
|
||||
return "TIMESTAMP";
|
||||
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource getDataSource() {
|
||||
|
||||
BasicDataSource dataSource = new BasicDataSource();
|
||||
|
||||
dataSource.setDriverClassName("org.h2.Driver");
|
||||
dataSource.setUrl("jdbc:h2:~/" + dbName);
|
||||
|
||||
return dataSource;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<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 UNIQUE, ExactTime TIMESTAMP, Msg BLOB)");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INT NOT NULL AUTO_INCREMENT PRIMARY KEY, Tag VARCHAR(50) UNIQUE)");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum INT, TagId INT,"
|
||||
+ " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum),"
|
||||
+ " FOREIGN KEY (TagId) REFERENCES TagTable(TagId),"
|
||||
+ " UNIQUE (EntryNum, TagID))");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INT, SignerId TINYBLOB, Signature TINYBLOB UNIQUE,"
|
||||
+ " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))");
|
||||
|
||||
list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)");
|
||||
list.add("CREATE UNIQUE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId, EntryNum)");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS BatchTable (SignerId TINYBLOB, BatchId INT, SerialNum INT, Data BLOB,"
|
||||
+ " UNIQUE(SignerId, BatchId, SerialNum))");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, TagId INT,"
|
||||
+ " FOREIGN KEY (TagId) REFERENCES TagTable(TagId))");
|
||||
|
||||
list.add("CREATE INDEX IF NOT EXISTS BatchIndex ON BatchTagTable(SignerId, BatchId)");
|
||||
|
||||
// This is used to create a simple table with one entry.
|
||||
// It is used for implementing a workaround for the missing INSERT IGNORE syntax
|
||||
list.add("CREATE TABLE IF NOT EXISTS UtilityTable (Entry INT)");
|
||||
list.add("INSERT INTO UtilityTable (Entry) VALUES (1)");
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSchemaDeletionCommands() {
|
||||
List<String> list = new LinkedList<String>();
|
||||
|
||||
list.add("DROP TABLE IF EXISTS UtilityTable");
|
||||
list.add("DROP INDEX IF EXISTS BatchIndex");
|
||||
list.add("DROP TABLE IF EXISTS BatchTagTable");
|
||||
list.add("DROP TABLE IF EXISTS BatchTable");
|
||||
list.add("DROP INDEX IF EXISTS SignerIdIndex");
|
||||
list.add("DROP TABLE IF EXISTS MsgTagTable");
|
||||
list.add("DROP TABLE IF EXISTS SignatureTable");
|
||||
list.add("DROP TABLE IF EXISTS TagTable");
|
||||
list.add("DROP TABLE IF EXISTS MsgTable");
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,148 +1,273 @@
|
|||
package meerkat.bulletinboard.sqlserver;
|
||||
|
||||
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider;
|
||||
import meerkat.protobuf.BulletinBoardAPI.FilterType;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||
*/
|
||||
|
||||
public class MySQLQueryProvider implements SQLQueryProvider {
|
||||
|
||||
private String dbAddress;
|
||||
private int dbPort;
|
||||
private String dbName;
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
public MySQLQueryProvider(String dbAddress, int dbPort, String dbName, String username, String password) {
|
||||
this.dbAddress = dbAddress;
|
||||
this.dbPort = dbPort;
|
||||
this.dbName = dbName;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSQLString(QueryType queryType) throws IllegalArgumentException{
|
||||
|
||||
switch(queryType) {
|
||||
case ADD_SIGNATURE:
|
||||
return "INSERT IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (:EntryNum, :SignerId, :Signature)";
|
||||
case CONNECT_TAG:
|
||||
return "INSERT 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 GET_MESSAGES:
|
||||
return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
|
||||
case GET_SIGNATURES:
|
||||
return "SELECT Signature FROM SignatureTable WHERE EntryNum = :EntryNum";
|
||||
case INSERT_MSG:
|
||||
return "INSERT INTO MsgTable (MsgId, Msg) VALUES(:MsgId, :Msg)";
|
||||
case INSERT_NEW_TAG:
|
||||
return "INSERT IGNORE INTO TagTable(Tag) VALUES (:Tag)";
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException {
|
||||
|
||||
String serialString = Integer.toString(serialNum);
|
||||
|
||||
switch(filterType) {
|
||||
case EXACT_ENTRY:
|
||||
return "MsgTable.EntryNum = :EntryNum" + serialString;
|
||||
case MAX_ENTRY:
|
||||
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
||||
case MAX_MESSAGES:
|
||||
return "LIMIT :Limit" + serialString;
|
||||
case MSG_ID:
|
||||
return "MsgTable.MsgId = :MsgId" + serialString;
|
||||
case SIGNER_ID:
|
||||
return "EXISTS (SELECT 1 FROM SignatureTable"
|
||||
+ " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)";
|
||||
case TAG:
|
||||
return "EXISTS (SELECT 1 FROM TagTable"
|
||||
+ " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId"
|
||||
+ " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)";
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException {
|
||||
|
||||
switch(filterType) {
|
||||
case EXACT_ENTRY: // Go through
|
||||
case MAX_ENTRY: // Go through
|
||||
case MAX_MESSAGES:
|
||||
return "INT";
|
||||
|
||||
case MSG_ID: // Go through
|
||||
case SIGNER_ID:
|
||||
return "TINYBLOB";
|
||||
|
||||
case TAG:
|
||||
return "VARCHAR";
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource getDataSource() {
|
||||
MysqlDataSource dataSource = new MysqlDataSource();
|
||||
|
||||
dataSource.setServerName(dbAddress);
|
||||
dataSource.setPort(dbPort);
|
||||
dataSource.setDatabaseName(dbName);
|
||||
dataSource.setUser(username);
|
||||
dataSource.setPassword(password);
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
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, Msg BLOB, UNIQUE(MsgId(50)))");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INT NOT NULL AUTO_INCREMENT PRIMARY KEY, Tag VARCHAR(50), UNIQUE(Tag))");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum INT, TagId INT,"
|
||||
+ " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum),"
|
||||
+ " CONSTRAINT FOREIGN KEY (TagId) REFERENCES TagTable(TagId),"
|
||||
+ " CONSTRAINT UNIQUE (EntryNum, TagID))");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INT, SignerId TINYBLOB, Signature TINYBLOB,"
|
||||
+ " INDEX(SignerId(32)), CONSTRAINT Uni UNIQUE(SignerId(32), EntryNum), CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))");
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSchemaDeletionCommands() {
|
||||
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 TagTable");
|
||||
list.add("DROP TABLE IF EXISTS MsgTable");
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
package meerkat.bulletinboard.sqlserver;
|
||||
|
||||
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
|
||||
import meerkat.bulletinboard.BulletinBoardConstants;
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider;
|
||||
import meerkat.protobuf.BulletinBoardAPI.FilterType;
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||
*/
|
||||
|
||||
public class MySQLQueryProvider implements SQLQueryProvider {
|
||||
|
||||
private String dbAddress;
|
||||
private int dbPort;
|
||||
private String dbName;
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
public MySQLQueryProvider(String dbAddress, int dbPort, String dbName, String username, String password) {
|
||||
this.dbAddress = dbAddress;
|
||||
this.dbPort = dbPort;
|
||||
this.dbName = dbName;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSQLString(QueryType queryType) throws IllegalArgumentException{
|
||||
|
||||
switch(queryType) {
|
||||
|
||||
case ADD_SIGNATURE:
|
||||
return MessageFormat.format(
|
||||
"INSERT IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (:{0}, :{1}, :{2})",
|
||||
QueryType.ADD_SIGNATURE.getParamName(0),
|
||||
QueryType.ADD_SIGNATURE.getParamName(1),
|
||||
QueryType.ADD_SIGNATURE.getParamName(2));
|
||||
|
||||
case CONNECT_TAG:
|
||||
return MessageFormat.format(
|
||||
"INSERT IGNORE INTO MsgTagTable (TagId, EntryNum)"
|
||||
+ " SELECT TagTable.TagId, :{0} AS EntryNum FROM TagTable WHERE Tag = :{1}",
|
||||
QueryType.CONNECT_TAG.getParamName(0),
|
||||
QueryType.CONNECT_TAG.getParamName(1));
|
||||
|
||||
case FIND_MSG_ID:
|
||||
return MessageFormat.format(
|
||||
"SELECT EntryNum From MsgTable WHERE MsgId = :{0}",
|
||||
QueryType.FIND_MSG_ID.getParamName(0));
|
||||
|
||||
case FIND_TAG_ID:
|
||||
return MessageFormat.format(
|
||||
"SELECT TagId FROM TagTable WHERE Tag = :{0}",
|
||||
QueryType.FIND_TAG_ID.getParamName(0));
|
||||
|
||||
case GET_MESSAGES:
|
||||
return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
|
||||
|
||||
case COUNT_MESSAGES:
|
||||
return "SELECT COUNT(MsgTable.EntryNum) FROM MsgTable";
|
||||
|
||||
case GET_MESSAGE_STUBS:
|
||||
return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable";
|
||||
|
||||
case GET_SIGNATURES:
|
||||
return MessageFormat.format(
|
||||
"SELECT Signature FROM SignatureTable WHERE EntryNum = :{0}",
|
||||
QueryType.GET_SIGNATURES.getParamName(0));
|
||||
|
||||
case INSERT_MSG:
|
||||
return MessageFormat.format(
|
||||
"INSERT INTO MsgTable (MsgId, ExactTime, Msg) VALUES(:{0}, :{1}, :{2})",
|
||||
QueryType.INSERT_MSG.getParamName(0),
|
||||
QueryType.INSERT_MSG.getParamName(1),
|
||||
QueryType.INSERT_MSG.getParamName(2));
|
||||
|
||||
case INSERT_NEW_TAG:
|
||||
return MessageFormat.format(
|
||||
"INSERT IGNORE INTO TagTable(Tag) VALUES (:{0})",
|
||||
QueryType.INSERT_NEW_TAG.getParamName(0));
|
||||
|
||||
case GET_LAST_MESSAGE_ENTRY:
|
||||
return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable";
|
||||
|
||||
case GET_BATCH_MESSAGE_ENTRY:
|
||||
return MessageFormat.format(
|
||||
"SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"
|
||||
+ " INNER JOIN SignatureTable ON MsgTable.EntryNum = SignatureTable.EntryNum"
|
||||
+ " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum"
|
||||
+ " INNER JOIN TagTable ON MsgTagTable.TagId = TagTable.TagId"
|
||||
+ " WHERE SignatureTable.SignerId = :{0}"
|
||||
+ " AND TagTable.Tag = :{1}",
|
||||
QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0),
|
||||
QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1));
|
||||
|
||||
case GET_BATCH_MESSAGE_DATA:
|
||||
return MessageFormat.format(
|
||||
"SELECT Data FROM BatchTable"
|
||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}"
|
||||
+ " ORDER BY SerialNum ASC",
|
||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
|
||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),
|
||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2));
|
||||
|
||||
case INSERT_BATCH_DATA:
|
||||
return MessageFormat.format(
|
||||
"INSERT INTO BatchTable (SignerId, BatchId, SerialNum, Data)"
|
||||
+ " VALUES (:{0}, :{1}, :{2}, :{3})",
|
||||
QueryType.INSERT_BATCH_DATA.getParamName(0),
|
||||
QueryType.INSERT_BATCH_DATA.getParamName(1),
|
||||
QueryType.INSERT_BATCH_DATA.getParamName(2),
|
||||
QueryType.INSERT_BATCH_DATA.getParamName(3));
|
||||
|
||||
case CHECK_BATCH_LENGTH:
|
||||
return MessageFormat.format(
|
||||
"SELECT COUNT(Data) AS BatchLength FROM BatchTable"
|
||||
+ " WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||
QueryType.CHECK_BATCH_LENGTH.getParamName(0),
|
||||
QueryType.CHECK_BATCH_LENGTH.getParamName(1));
|
||||
|
||||
case CONNECT_BATCH_TAG:
|
||||
return MessageFormat.format(
|
||||
"INSERT INTO BatchTagTable (SignerId, BatchId, TagId) SELECT :{0}, :{1}, TagId FROM TagTable"
|
||||
+ " WHERE Tag = :{2}",
|
||||
QueryType.CONNECT_BATCH_TAG.getParamName(0),
|
||||
QueryType.CONNECT_BATCH_TAG.getParamName(1),
|
||||
QueryType.CONNECT_BATCH_TAG.getParamName(2));
|
||||
|
||||
case GET_BATCH_TAGS:
|
||||
return MessageFormat.format(
|
||||
"SELECT Tag FROM TagTable INNER JOIN BatchTagTable ON TagTable.TagId = BatchTagTable.TagId"
|
||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} ORDER BY Tag ASC",
|
||||
QueryType.GET_BATCH_TAGS.getParamName(0),
|
||||
QueryType.GET_BATCH_TAGS.getParamName(1));
|
||||
|
||||
case REMOVE_BATCH_TAGS:
|
||||
return MessageFormat.format(
|
||||
"DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||
QueryType.REMOVE_BATCH_TAGS.getParamName(0),
|
||||
QueryType.REMOVE_BATCH_TAGS.getParamName(1));
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException {
|
||||
|
||||
String serialString = Integer.toString(serialNum);
|
||||
|
||||
switch(filterType) {
|
||||
case EXACT_ENTRY:
|
||||
return "MsgTable.EntryNum = :EntryNum" + serialString;
|
||||
case MAX_ENTRY:
|
||||
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
||||
case MIN_ENTRY:
|
||||
return "MsgTable.EntryNum >= :EntryNum" + serialString;
|
||||
case MAX_MESSAGES:
|
||||
return "LIMIT :Limit" + serialString;
|
||||
case MSG_ID:
|
||||
return "MsgTable.MsgId = :MsgId" + serialString;
|
||||
case SIGNER_ID:
|
||||
return "EXISTS (SELECT 1 FROM SignatureTable"
|
||||
+ " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)";
|
||||
case TAG:
|
||||
return "EXISTS (SELECT 1 FROM TagTable"
|
||||
+ " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId"
|
||||
+ " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)";
|
||||
|
||||
case BEFORE_TIME:
|
||||
return "MsgTable.ExactTime <= :TimeStamp";
|
||||
|
||||
case AFTER_TIME:
|
||||
return "MsgTable.ExactTime >= :TimeStamp";
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException {
|
||||
|
||||
switch(filterType) {
|
||||
case EXACT_ENTRY: // Go through
|
||||
case MAX_ENTRY: // Go through
|
||||
case MIN_ENTRY: // Go through
|
||||
case MAX_MESSAGES:
|
||||
return "INT";
|
||||
|
||||
case MSG_ID: // Go through
|
||||
case SIGNER_ID:
|
||||
return "TINYBLOB";
|
||||
|
||||
case TAG:
|
||||
return "VARCHAR";
|
||||
|
||||
case AFTER_TIME: // Go through
|
||||
case BEFORE_TIME:
|
||||
return "TIMESTAMP";
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource getDataSource() {
|
||||
|
||||
BasicDataSource dataSource = new BasicDataSource();
|
||||
|
||||
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
|
||||
dataSource.setUrl("jdbc:mysql://" + dbAddress + ":" + dbPort + "/" + dbName);
|
||||
|
||||
dataSource.setUsername(username);
|
||||
dataSource.setPassword(password);
|
||||
|
||||
return dataSource;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<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 TagTable (TagId INT NOT NULL AUTO_INCREMENT PRIMARY KEY, Tag VARCHAR(50), UNIQUE(Tag))");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum INT, TagId INT,"
|
||||
+ " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum),"
|
||||
+ " CONSTRAINT FOREIGN KEY (TagId) REFERENCES TagTable(TagId),"
|
||||
+ " CONSTRAINT UNIQUE (EntryNum, TagID))");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INT, SignerId TINYBLOB, Signature TINYBLOB,"
|
||||
+ " INDEX(SignerId(32)), CONSTRAINT Unique_Signature UNIQUE(SignerId(32), EntryNum),"
|
||||
+ " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS BatchTable (SignerId TINYBLOB, BatchId INT, SerialNum INT, Data BLOB,"
|
||||
+ " CONSTRAINT Unique_Batch UNIQUE(SignerId(32), BatchId, SerialNum))");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, TagId INT,"
|
||||
+ " INDEX(SignerId(32), BatchId), CONSTRAINT FOREIGN KEY (TagId) REFERENCES TagTable(TagId))");
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSchemaDeletionCommands() {
|
||||
List<String> list = new LinkedList<String>();
|
||||
|
||||
list.add("DROP TABLE IF EXISTS BatchTagTable");
|
||||
list.add("DROP TABLE IF EXISTS BatchTable");
|
||||
list.add("DROP TABLE IF EXISTS MsgTagTable");
|
||||
list.add("DROP TABLE IF EXISTS SignatureTable");
|
||||
list.add("DROP TABLE IF EXISTS TagTable");
|
||||
list.add("DROP TABLE IF EXISTS MsgTable");
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,123 +1,233 @@
|
|||
package meerkat.bulletinboard.sqlserver;
|
||||
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import org.sqlite.SQLiteDataSource;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||
*/
|
||||
|
||||
public class SQLiteQueryProvider implements BulletinBoardSQLServer.SQLQueryProvider {
|
||||
|
||||
String dbName;
|
||||
|
||||
public SQLiteQueryProvider(String dbName) {
|
||||
this.dbName = dbName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSQLString(QueryType queryType) throws IllegalArgumentException{
|
||||
|
||||
switch(queryType) {
|
||||
case ADD_SIGNATURE:
|
||||
return "INSERT OR IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (:EntryNum,:SignerId,:Signature)";
|
||||
case CONNECT_TAG:
|
||||
return "INSERT OR IGNORE INTO MsgTagTable (TagId, EntryNum)"
|
||||
+ " SELECT TagTable.TagId, :EntryNum AS EntryNum FROM TagTable WHERE Tag = :Tag";
|
||||
case FIND_MSG_ID:
|
||||
return "SELECT EntryNum From MsgTable WHERE MsgId = :MsgId";
|
||||
case GET_MESSAGES:
|
||||
return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
|
||||
case GET_SIGNATURES:
|
||||
return "SELECT Signature FROM SignatureTable WHERE EntryNum = :EntryNum";
|
||||
case INSERT_MSG:
|
||||
return "INSERT INTO MsgTable (MsgId, Msg) VALUES(:MsgId,:Msg)";
|
||||
case INSERT_NEW_TAG:
|
||||
return "INSERT OR IGNORE INTO TagTable(Tag) VALUES (:Tag)";
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException {
|
||||
|
||||
String serialString = Integer.toString(serialNum);
|
||||
|
||||
switch(filterType) {
|
||||
case EXACT_ENTRY:
|
||||
return "MsgTable.EntryNum = :EntryNum" + serialString;
|
||||
case MAX_ENTRY:
|
||||
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
||||
case MAX_MESSAGES:
|
||||
return "LIMIT = :Limit" + serialString;
|
||||
case MSG_ID:
|
||||
return "MsgTable.MsgId = :MsgId" + serialString;
|
||||
case SIGNER_ID:
|
||||
return "EXISTS (SELECT 1 FROM SignatureTable"
|
||||
+ " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)";
|
||||
case TAG:
|
||||
return "EXISTS (SELECT 1 FROM TagTable"
|
||||
+ " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId"
|
||||
+ " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)";
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException {
|
||||
return null; //TODO: write this.
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource getDataSource() {
|
||||
// TODO: Fix this
|
||||
SQLiteDataSource dataSource = new SQLiteDataSource();
|
||||
dataSource.setUrl("jdbc:sqlite:" + dbName);
|
||||
dataSource.setDatabaseName("meerkat"); //TODO: Make generic
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
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)");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INTEGER PRIMARY KEY, Tag varchar(50) UNIQUE)");
|
||||
list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum BLOB, TagId INTEGER, FOREIGN KEY (EntryNum)"
|
||||
+ " REFERENCES MsgTable(EntryNum), FOREIGN KEY (TagId) REFERENCES TagTable(TagId), UNIQUE (EntryNum, TagID))");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INTEGER, SignerId BLOB, Signature BLOB,"
|
||||
+ " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))");
|
||||
|
||||
list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)");
|
||||
list.add("CREATE UNIQUE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId, EntryNum)");
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSchemaDeletionCommands() {
|
||||
List<String> list = new LinkedList<String>();
|
||||
|
||||
list.add("DROP TABLE IF EXISTS MsgTagTable");
|
||||
|
||||
list.add("DROP INDEX IF EXISTS SignerIndex");
|
||||
list.add("DROP TABLE IF EXISTS SignatureTable");
|
||||
|
||||
list.add("DROP TABLE IF EXISTS TagTable");
|
||||
list.add("DROP TABLE IF EXISTS MsgTable");
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
package meerkat.bulletinboard.sqlserver;
|
||||
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import org.sqlite.SQLiteDataSource;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 09-Dec-15.
|
||||
*/
|
||||
|
||||
public class SQLiteQueryProvider implements BulletinBoardSQLServer.SQLQueryProvider {
|
||||
|
||||
String dbName;
|
||||
|
||||
public SQLiteQueryProvider(String dbName) {
|
||||
this.dbName = dbName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSQLString(QueryType queryType) throws IllegalArgumentException{
|
||||
|
||||
switch(queryType) {
|
||||
case ADD_SIGNATURE:
|
||||
return "INSERT OR IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (:EntryNum,:SignerId,:Signature)";
|
||||
|
||||
case CONNECT_TAG:
|
||||
return "INSERT OR IGNORE INTO MsgTagTable (TagId, EntryNum)"
|
||||
+ " SELECT TagTable.TagId, :EntryNum AS EntryNum FROM TagTable WHERE Tag = :Tag";
|
||||
|
||||
case FIND_MSG_ID:
|
||||
return "SELECT EntryNum From MsgTable WHERE MsgId = :MsgId";
|
||||
|
||||
case FIND_TAG_ID:
|
||||
return MessageFormat.format(
|
||||
"SELECT TagId FROM TagTable WHERE Tag = :{0}",
|
||||
QueryType.FIND_TAG_ID.getParamName(0));
|
||||
|
||||
case GET_MESSAGES:
|
||||
return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
|
||||
|
||||
case COUNT_MESSAGES:
|
||||
return "SELECT COUNT(MsgTable.EntryNum) FROM MsgTable";
|
||||
|
||||
case GET_MESSAGE_STUBS:
|
||||
return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable";
|
||||
|
||||
case GET_SIGNATURES:
|
||||
return "SELECT Signature FROM SignatureTable WHERE EntryNum = :EntryNum";
|
||||
|
||||
case INSERT_MSG:
|
||||
return "INSERT INTO MsgTable (MsgId, Msg) VALUES(:MsgId,:Msg)";
|
||||
|
||||
case INSERT_NEW_TAG:
|
||||
return "INSERT OR IGNORE INTO TagTable(Tag) VALUES (:Tag)";
|
||||
|
||||
case GET_LAST_MESSAGE_ENTRY:
|
||||
return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable";
|
||||
|
||||
case GET_BATCH_MESSAGE_ENTRY:
|
||||
return MessageFormat.format(
|
||||
"SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"
|
||||
+ " INNER JOIN SignatureTable ON MsgTable.EntryNum = SignatureTable.EntryNum"
|
||||
+ " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum"
|
||||
+ " INNER JOIN TagTable ON MsgTagTable.TagId = TagTable.TagId"
|
||||
+ " WHERE SignatureTable.SignerId = :{0}"
|
||||
+ " AND TagTable.Tag = :{1}",
|
||||
QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0),
|
||||
QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1));
|
||||
|
||||
case GET_BATCH_MESSAGE_DATA:
|
||||
return MessageFormat.format(
|
||||
"SELECT Data FROM BatchTable"
|
||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}"
|
||||
+ " ORDER BY SerialNum ASC",
|
||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
|
||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),
|
||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2));
|
||||
|
||||
case INSERT_BATCH_DATA:
|
||||
return MessageFormat.format(
|
||||
"INSERT INTO BatchTable (SignerId, BatchId, SerialNum, Data)"
|
||||
+ " VALUES (:{0}, :{1}, :{2}, :{3})",
|
||||
QueryType.INSERT_BATCH_DATA.getParamName(0),
|
||||
QueryType.INSERT_BATCH_DATA.getParamName(1),
|
||||
QueryType.INSERT_BATCH_DATA.getParamName(2),
|
||||
QueryType.INSERT_BATCH_DATA.getParamName(3));
|
||||
|
||||
case CHECK_BATCH_LENGTH:
|
||||
return MessageFormat.format(
|
||||
"SELECT COUNT(Data) AS BatchLength FROM BatchTable"
|
||||
+ " WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||
QueryType.CHECK_BATCH_LENGTH.getParamName(0),
|
||||
QueryType.CHECK_BATCH_LENGTH.getParamName(1));
|
||||
|
||||
case CONNECT_BATCH_TAG:
|
||||
return MessageFormat.format(
|
||||
"INSERT INTO BatchTagTable (SignerId, BatchId, TagId) SELECT :{0}, :{1}, TagId FROM TagTable"
|
||||
+ " WHERE Tag = :{2}",
|
||||
QueryType.CONNECT_BATCH_TAG.getParamName(0),
|
||||
QueryType.CONNECT_BATCH_TAG.getParamName(1),
|
||||
QueryType.CONNECT_BATCH_TAG.getParamName(2));
|
||||
|
||||
case GET_BATCH_TAGS:
|
||||
return MessageFormat.format(
|
||||
"SELECT Tag FROM TagTable INNER JOIN BatchTagTable ON TagTable.TagId = BatchTagTable.TagId"
|
||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} ORDER BY Tag ASC",
|
||||
QueryType.GET_BATCH_TAGS.getParamName(0),
|
||||
QueryType.GET_BATCH_TAGS.getParamName(1));
|
||||
|
||||
case REMOVE_BATCH_TAGS:
|
||||
return MessageFormat.format(
|
||||
"DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||
QueryType.REMOVE_BATCH_TAGS.getParamName(0),
|
||||
QueryType.REMOVE_BATCH_TAGS.getParamName(1));
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException {
|
||||
|
||||
String serialString = Integer.toString(serialNum);
|
||||
|
||||
switch(filterType) {
|
||||
case EXACT_ENTRY:
|
||||
return "MsgTable.EntryNum = :EntryNum" + serialString;
|
||||
|
||||
case MAX_ENTRY:
|
||||
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
||||
|
||||
case MIN_ENTRY:
|
||||
return "MsgTable.EntryNum <= :EntryNum" + serialString;
|
||||
|
||||
case MAX_MESSAGES:
|
||||
return "LIMIT = :Limit" + serialString;
|
||||
|
||||
case MSG_ID:
|
||||
return "MsgTable.MsgId = :MsgId" + serialString;
|
||||
|
||||
case SIGNER_ID:
|
||||
return "EXISTS (SELECT 1 FROM SignatureTable"
|
||||
+ " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)";
|
||||
|
||||
case TAG:
|
||||
return "EXISTS (SELECT 1 FROM TagTable"
|
||||
+ " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId"
|
||||
+ " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)";
|
||||
|
||||
case BEFORE_TIME:
|
||||
return "MsgTable.ExactTime <= :TimeStamp";
|
||||
|
||||
case AFTER_TIME:
|
||||
return "MsgTable.ExactTime >= :TimeStamp";
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException {
|
||||
|
||||
switch(filterType) {
|
||||
case EXACT_ENTRY: // Go through
|
||||
case MAX_ENTRY: // Go through
|
||||
case MIN_ENTRY: // Go through
|
||||
case MAX_MESSAGES:
|
||||
return "INTEGER";
|
||||
|
||||
case MSG_ID: // Go through
|
||||
case SIGNER_ID:
|
||||
return "BLOB";
|
||||
|
||||
case TAG:
|
||||
return "VARCHAR";
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource getDataSource() {
|
||||
|
||||
SQLiteDataSource dataSource = new SQLiteDataSource();
|
||||
dataSource.setUrl("jdbc:sqlite:" + dbName);
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<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)");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INTEGER PRIMARY KEY, Tag varchar(50) UNIQUE)");
|
||||
list.add("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum BLOB, TagId INTEGER, FOREIGN KEY (EntryNum)"
|
||||
+ " REFERENCES MsgTable(EntryNum), FOREIGN KEY (TagId) REFERENCES TagTable(TagId), UNIQUE (EntryNum, TagID))");
|
||||
|
||||
list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INTEGER, SignerId BLOB, Signature BLOB,"
|
||||
+ " FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum), UNIQUE(SignerId, EntryNum))");
|
||||
|
||||
list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)");
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSchemaDeletionCommands() {
|
||||
List<String> list = new LinkedList<String>();
|
||||
|
||||
list.add("DROP TABLE IF EXISTS MsgTagTable");
|
||||
|
||||
list.add("DROP INDEX IF EXISTS SignerIndex");
|
||||
list.add("DROP TABLE IF EXISTS SignatureTable");
|
||||
|
||||
list.add("DROP TABLE IF EXISTS TagTable");
|
||||
list.add("DROP TABLE IF EXISTS MsgTable");
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package meerkat.bulletinboard.sqlserver.mappers;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import meerkat.comm.MessageOutputStream;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BatchData;
|
||||
import org.springframework.jdbc.core.RowCallbackHandler;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 19-Dec-15.
|
||||
*/
|
||||
public class BatchDataCallbackHandler implements RowCallbackHandler {
|
||||
|
||||
private final MessageOutputStream<BatchData> out;
|
||||
|
||||
public BatchDataCallbackHandler(MessageOutputStream<BatchData> out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processRow(ResultSet rs) throws SQLException {
|
||||
try {
|
||||
out.writeMessage(BatchData.parseFrom(rs.getBytes(1)));
|
||||
} catch (IOException e) {
|
||||
//TODO: Log
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,18 +1,18 @@
|
|||
package meerkat.bulletinboard.sqlserver.mappers;
|
||||
|
||||
import meerkat.protobuf.BulletinBoardAPI.MessageID;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 11-Dec-15.
|
||||
*/
|
||||
public class EntryNumMapper implements RowMapper<Long> {
|
||||
|
||||
@Override
|
||||
public Long mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
return rs.getLong(1);
|
||||
}
|
||||
}
|
||||
package meerkat.bulletinboard.sqlserver.mappers;
|
||||
|
||||
import meerkat.protobuf.BulletinBoardAPI.MessageID;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 11-Dec-15.
|
||||
*/
|
||||
public class LongMapper implements RowMapper<Long> {
|
||||
|
||||
@Override
|
||||
public Long mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
return rs.getLong(1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package meerkat.bulletinboard.sqlserver.mappers;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.*;
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider.*;
|
||||
import meerkat.comm.MessageOutputStream;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import org.springframework.jdbc.core.RowCallbackHandler;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 21-Feb-16.
|
||||
*/
|
||||
public class MessageCallbackHandler implements RowCallbackHandler {
|
||||
|
||||
private final NamedParameterJdbcTemplate jdbcTemplate;
|
||||
private final SQLQueryProvider sqlQueryProvider;
|
||||
private final MessageOutputStream<BulletinBoardMessage> out;
|
||||
|
||||
public MessageCallbackHandler(NamedParameterJdbcTemplate jdbcTemplate, SQLQueryProvider sqlQueryProvider, MessageOutputStream<BulletinBoardMessage> out) {
|
||||
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
this.sqlQueryProvider = sqlQueryProvider;
|
||||
this.out = out;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processRow(ResultSet rs) throws SQLException {
|
||||
|
||||
BulletinBoardMessage.Builder result;
|
||||
|
||||
try {
|
||||
|
||||
result = BulletinBoardMessage.newBuilder()
|
||||
.setEntryNum(rs.getLong(1))
|
||||
.setMsg(UnsignedBulletinBoardMessage.parseFrom(rs.getBytes(2)));
|
||||
|
||||
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
//TODO: log
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve signatures
|
||||
|
||||
MapSqlParameterSource sqlParameterSource = new MapSqlParameterSource();
|
||||
sqlParameterSource.addValue(QueryType.GET_SIGNATURES.getParamName(0), result.getEntryNum());
|
||||
|
||||
List<Crypto.Signature> signatures = jdbcTemplate.query(
|
||||
sqlQueryProvider.getSQLString(QueryType.GET_SIGNATURES),
|
||||
sqlParameterSource,
|
||||
new SignatureMapper());
|
||||
|
||||
// Append signatures
|
||||
result.addAllSig(signatures);
|
||||
|
||||
// Finalize message and add to message list.
|
||||
|
||||
try {
|
||||
|
||||
out.writeMessage(result.build());
|
||||
|
||||
} catch (IOException e) {
|
||||
|
||||
//TODO: log
|
||||
e.printStackTrace();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,32 +1,32 @@
|
|||
package meerkat.bulletinboard.sqlserver.mappers;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 11-Dec-15.
|
||||
*/
|
||||
public class MessageMapper implements RowMapper<BulletinBoardMessage.Builder> {
|
||||
|
||||
@Override
|
||||
public BulletinBoardMessage.Builder mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
|
||||
BulletinBoardMessage.Builder builder = BulletinBoardMessage.newBuilder();
|
||||
|
||||
try {
|
||||
builder.setEntryNum(rs.getLong(1))
|
||||
.setMsg(UnsignedBulletinBoardMessage.parseFrom(rs.getBytes(2)));
|
||||
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new SQLException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
package meerkat.bulletinboard.sqlserver.mappers;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 11-Dec-15.
|
||||
*/
|
||||
public class MessageMapper implements RowMapper<BulletinBoardMessage.Builder> {
|
||||
|
||||
@Override
|
||||
public BulletinBoardMessage.Builder mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
|
||||
BulletinBoardMessage.Builder builder = BulletinBoardMessage.newBuilder();
|
||||
|
||||
try {
|
||||
builder.setEntryNum(rs.getLong(1))
|
||||
.setMsg(UnsignedBulletinBoardMessage.parseFrom(rs.getBytes(2)));
|
||||
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new SQLException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package meerkat.bulletinboard.sqlserver.mappers;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider.QueryType;
|
||||
import meerkat.comm.MessageOutputStream;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import meerkat.util.BulletinBoardUtils;
|
||||
import org.springframework.jdbc.core.RowCallbackHandler;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 21-Feb-16.
|
||||
*/
|
||||
public class MessageStubCallbackHandler implements RowCallbackHandler {
|
||||
|
||||
private final MessageOutputStream<BulletinBoardMessage> out;
|
||||
|
||||
public MessageStubCallbackHandler(MessageOutputStream<BulletinBoardMessage> out) {
|
||||
|
||||
this.out = out;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processRow(ResultSet rs) throws SQLException {
|
||||
|
||||
BulletinBoardMessage result;
|
||||
|
||||
result = BulletinBoardMessage.newBuilder()
|
||||
.setEntryNum(rs.getLong(1))
|
||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||
.setData(ByteString.copyFrom(rs.getBytes(2)))
|
||||
.setTimestamp(BulletinBoardUtils.toTimestampProto(rs.getTimestamp(3)))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
try {
|
||||
|
||||
out.writeMessage(result);
|
||||
|
||||
} catch (IOException e) {
|
||||
|
||||
//TODO: log
|
||||
e.printStackTrace();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package meerkat.bulletinboard.sqlserver.mappers;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
|
||||
import meerkat.util.BulletinBoardUtils;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 11-Dec-15.
|
||||
*/
|
||||
public class MessageStubMapper implements RowMapper<BulletinBoardMessage> {
|
||||
|
||||
@Override
|
||||
public BulletinBoardMessage mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
|
||||
return BulletinBoardMessage.newBuilder()
|
||||
.setEntryNum(rs.getLong(1))
|
||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||
.setData(ByteString.copyFrom(rs.getBytes(2)))
|
||||
.setTimestamp(BulletinBoardUtils.toTimestampProto(rs.getTimestamp(3)))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,28 +1,28 @@
|
|||
package meerkat.bulletinboard.sqlserver.mappers;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
|
||||
import meerkat.protobuf.Crypto.Signature;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 11-Dec-15.
|
||||
*/
|
||||
public class SignatureMapper implements RowMapper<Signature> {
|
||||
|
||||
@Override
|
||||
public Signature mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
|
||||
try {
|
||||
return Signature.parseFrom(rs.getBytes(1));
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new SQLException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
package meerkat.bulletinboard.sqlserver.mappers;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
|
||||
import meerkat.protobuf.Crypto.Signature;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 11-Dec-15.
|
||||
*/
|
||||
public class SignatureMapper implements RowMapper<Signature> {
|
||||
|
||||
@Override
|
||||
public Signature mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
|
||||
try {
|
||||
return Signature.parseFrom(rs.getBytes(1));
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new SQLException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package meerkat.bulletinboard.sqlserver.mappers;
|
||||
|
||||
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 StringMapper implements RowMapper<String> {
|
||||
|
||||
@Override
|
||||
public String mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
return rs.getString(1);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,124 +1,266 @@
|
|||
package meerkat.bulletinboard.webapp;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import meerkat.bulletinboard.BulletinBoardServer;
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
||||
import meerkat.bulletinboard.sqlserver.H2QueryProvider;
|
||||
import meerkat.bulletinboard.sqlserver.MySQLQueryProvider;
|
||||
import meerkat.bulletinboard.sqlserver.SQLiteQueryProvider;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BoolMsg;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessageList;
|
||||
import meerkat.protobuf.BulletinBoardAPI.MessageFilterList;
|
||||
import meerkat.rest.Constants;
|
||||
|
||||
@Path(Constants.BULLETIN_BOARD_SERVER_PATH)
|
||||
public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextListener{
|
||||
|
||||
private static final String BULLETIN_BOARD_ATTRIBUTE_NAME = "bulletinBoard";
|
||||
|
||||
@Context ServletContext servletContext;
|
||||
|
||||
BulletinBoardServer bulletinBoard;
|
||||
|
||||
/**
|
||||
* This is the servlet init method.
|
||||
*/
|
||||
public void init(){
|
||||
bulletinBoard = (BulletinBoardServer) servletContext.getAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the BulletinBoard init method.
|
||||
*/
|
||||
@Override
|
||||
public void init(String meerkatDB) throws CommunicationException {
|
||||
bulletinBoard.init(meerkatDB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent servletContextEvent) {
|
||||
ServletContext servletContext = servletContextEvent.getServletContext();
|
||||
String dbType = servletContext.getInitParameter("dbType");
|
||||
String dbName = servletContext.getInitParameter("dbName");
|
||||
|
||||
if ("SQLite".equals(dbType)){
|
||||
|
||||
bulletinBoard = new BulletinBoardSQLServer(new SQLiteQueryProvider(dbName));
|
||||
|
||||
} else if ("H2".equals(dbType)) {
|
||||
|
||||
bulletinBoard = new BulletinBoardSQLServer(new H2QueryProvider(dbName));
|
||||
|
||||
} else if ("MySQL".equals(dbType)) {
|
||||
|
||||
String dbAddress = servletContext.getInitParameter("dbAddress");
|
||||
int dbPort = Integer.parseInt(servletContext.getInitParameter("dbPort"));
|
||||
String username = servletContext.getInitParameter("username");
|
||||
String password = servletContext.getInitParameter("password");
|
||||
|
||||
bulletinBoard = new BulletinBoardSQLServer(new MySQLQueryProvider(dbAddress,dbPort,dbName,username,password));
|
||||
}
|
||||
|
||||
try {
|
||||
init(dbName);
|
||||
servletContext.setAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME, bulletinBoard);
|
||||
} catch (CommunicationException e) {
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Path(Constants.POST_MESSAGE_PATH)
|
||||
@POST
|
||||
@Consumes(Constants.MEDIATYPE_PROTOBUF)
|
||||
@Produces(Constants.MEDIATYPE_PROTOBUF)
|
||||
@Override
|
||||
public BoolMsg postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||
init();
|
||||
return bulletinBoard.postMessage(msg);
|
||||
}
|
||||
|
||||
@Path(Constants.READ_MESSAGES_PATH)
|
||||
@POST
|
||||
@Consumes(Constants.MEDIATYPE_PROTOBUF)
|
||||
@Produces(Constants.MEDIATYPE_PROTOBUF)
|
||||
@Override
|
||||
public BulletinBoardMessageList readMessages(MessageFilterList filterList) throws CommunicationException {
|
||||
init();
|
||||
return bulletinBoard.readMessages(filterList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(){
|
||||
try {
|
||||
bulletinBoard.close();
|
||||
} catch (CommunicationException e) {
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public String test() {
|
||||
return "This BulletinBoard is up and running!\n Please consult the API documents to perform queries.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent servletContextEvent) {
|
||||
ServletContext servletContext = servletContextEvent.getServletContext();
|
||||
bulletinBoard = (BulletinBoardServer) servletContext.getAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME);
|
||||
close();
|
||||
}
|
||||
|
||||
}
|
||||
package meerkat.bulletinboard.webapp;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.ws.rs.*;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.StreamingOutput;
|
||||
|
||||
import com.google.protobuf.BoolValue;
|
||||
import meerkat.bulletinboard.BulletinBoardServer;
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
||||
import meerkat.bulletinboard.sqlserver.H2QueryProvider;
|
||||
import meerkat.bulletinboard.sqlserver.MySQLQueryProvider;
|
||||
import meerkat.bulletinboard.sqlserver.SQLiteQueryProvider;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.comm.MessageOutputStream;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.protobuf.Comm.*;
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.*;
|
||||
import static meerkat.rest.Constants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* An implementation of the BulletinBoardServer which functions as a WebApp
|
||||
*/
|
||||
@Path(BULLETIN_BOARD_SERVER_PATH)
|
||||
public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextListener{
|
||||
|
||||
private static final String BULLETIN_BOARD_ATTRIBUTE_NAME = "bulletinBoard";
|
||||
|
||||
@Context ServletContext servletContext;
|
||||
|
||||
BulletinBoardServer bulletinBoard;
|
||||
|
||||
/**
|
||||
* This is the servlet init method.
|
||||
*/
|
||||
public void init(){
|
||||
bulletinBoard = (BulletinBoardServer) servletContext.getAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the BulletinBoard init method.
|
||||
*/
|
||||
@Override
|
||||
public void init(String meerkatDB) throws CommunicationException {
|
||||
bulletinBoard.init(meerkatDB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent servletContextEvent) {
|
||||
ServletContext servletContext = servletContextEvent.getServletContext();
|
||||
String dbType = servletContext.getInitParameter("dbType");
|
||||
String dbName = servletContext.getInitParameter("dbName");
|
||||
|
||||
if ("SQLite".equals(dbType)){
|
||||
|
||||
bulletinBoard = new BulletinBoardSQLServer(new SQLiteQueryProvider(dbName));
|
||||
|
||||
} else if ("H2".equals(dbType)) {
|
||||
|
||||
bulletinBoard = new BulletinBoardSQLServer(new H2QueryProvider(dbName));
|
||||
|
||||
} else if ("MySQL".equals(dbType)) {
|
||||
|
||||
String dbAddress = servletContext.getInitParameter("dbAddress");
|
||||
int dbPort = Integer.parseInt(servletContext.getInitParameter("dbPort"));
|
||||
String username = servletContext.getInitParameter("username");
|
||||
String password = servletContext.getInitParameter("password");
|
||||
|
||||
bulletinBoard = new BulletinBoardSQLServer(new MySQLQueryProvider(dbAddress,dbPort,dbName,username,password));
|
||||
}
|
||||
|
||||
try {
|
||||
init(dbName);
|
||||
servletContext.setAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME, bulletinBoard);
|
||||
} catch (CommunicationException e) {
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Path(POST_MESSAGE_PATH)
|
||||
@POST
|
||||
@Consumes(MEDIATYPE_PROTOBUF)
|
||||
@Produces(MEDIATYPE_PROTOBUF)
|
||||
@Override
|
||||
public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||
init();
|
||||
return bulletinBoard.postMessage(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readMessages(MessageFilterList filterList, MessageOutputStream<BulletinBoardMessage> out) throws CommunicationException {
|
||||
init();
|
||||
bulletinBoard.readMessages(filterList, out);
|
||||
}
|
||||
|
||||
|
||||
@Path(READ_MESSAGES_PATH)
|
||||
@POST
|
||||
@Consumes(MEDIATYPE_PROTOBUF)
|
||||
/**
|
||||
* Wrapper for the readMessages method which streams the output into the response
|
||||
*/
|
||||
public StreamingOutput readMessages(final MessageFilterList filterList) {
|
||||
|
||||
return new StreamingOutput() {
|
||||
|
||||
@Override
|
||||
public void write(OutputStream output) throws IOException, WebApplicationException {
|
||||
MessageOutputStream<BulletinBoardMessage> out = new MessageOutputStream<>(output);
|
||||
|
||||
try {
|
||||
init();
|
||||
bulletinBoard.readMessages(filterList, out);
|
||||
} catch (CommunicationException e) {
|
||||
//TODO: Log
|
||||
out.writeMessage(null);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@Path(BEGIN_BATCH_PATH)
|
||||
@POST
|
||||
@Consumes(MEDIATYPE_PROTOBUF)
|
||||
@Produces(MEDIATYPE_PROTOBUF)
|
||||
@Override
|
||||
public BoolValue beginBatch(BeginBatchMessage message) {
|
||||
try {
|
||||
init();
|
||||
return bulletinBoard.beginBatch(message);
|
||||
} catch (CommunicationException e) {
|
||||
System.err.println(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Path(POST_BATCH_PATH)
|
||||
@POST
|
||||
@Consumes(MEDIATYPE_PROTOBUF)
|
||||
@Produces(MEDIATYPE_PROTOBUF)
|
||||
@Override
|
||||
public BoolValue postBatchMessage(BatchMessage batchMessage) {
|
||||
try {
|
||||
init();
|
||||
return bulletinBoard.postBatchMessage(batchMessage);
|
||||
} catch (CommunicationException e) {
|
||||
System.err.println(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Path(CLOSE_BATCH_PATH)
|
||||
@POST
|
||||
@Consumes(MEDIATYPE_PROTOBUF)
|
||||
@Produces(MEDIATYPE_PROTOBUF)
|
||||
@Override
|
||||
public BoolValue closeBatchMessage(CloseBatchMessage message) {
|
||||
try {
|
||||
init();
|
||||
return bulletinBoard.closeBatchMessage(message);
|
||||
} catch (CommunicationException e) {
|
||||
System.err.println(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void readBatch(BatchSpecificationMessage message, MessageOutputStream<BatchData> out) {
|
||||
try {
|
||||
init();
|
||||
bulletinBoard.readBatch(message, out);
|
||||
} catch (CommunicationException | IllegalArgumentException e) {
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Path(GENERATE_SYNC_QUERY_PATH)
|
||||
@POST
|
||||
@Consumes(MEDIATYPE_PROTOBUF)
|
||||
@Produces(MEDIATYPE_PROTOBUF)
|
||||
@Override
|
||||
public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException {
|
||||
try {
|
||||
init();
|
||||
return bulletinBoard.generateSyncQuery(generateSyncQueryParams);
|
||||
} catch (CommunicationException | IllegalArgumentException e) {
|
||||
System.err.println(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Path(READ_BATCH_PATH)
|
||||
@POST
|
||||
@Consumes(MEDIATYPE_PROTOBUF)
|
||||
/**
|
||||
* Wrapper for the readBatch method which streams the output into the response
|
||||
*/
|
||||
public StreamingOutput readBatch(final BatchSpecificationMessage message) {
|
||||
|
||||
return new StreamingOutput() {
|
||||
|
||||
@Override
|
||||
public void write(OutputStream output) throws IOException, WebApplicationException {
|
||||
MessageOutputStream<BatchData> out = new MessageOutputStream<>(output);
|
||||
|
||||
try {
|
||||
init();
|
||||
bulletinBoard.readBatch(message, out);
|
||||
} catch (CommunicationException e) {
|
||||
//TODO: Log
|
||||
out.writeMessage(null);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@Path(SYNC_QUERY_PATH)
|
||||
@POST
|
||||
@Consumes(MEDIATYPE_PROTOBUF)
|
||||
@Produces(MEDIATYPE_PROTOBUF)
|
||||
@Override
|
||||
public SyncQueryResponse querySync(SyncQuery syncQuery) throws CommunicationException {
|
||||
try{
|
||||
init();
|
||||
return bulletinBoard.querySync(syncQuery);
|
||||
} catch (CommunicationException | IllegalArgumentException e) {
|
||||
System.err.println(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(){
|
||||
try {
|
||||
bulletinBoard.close();
|
||||
} catch (CommunicationException e) {
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public String test() {
|
||||
return "This BulletinBoard is up and running!\n Please consult the API documents to perform queries.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent servletContextEvent) {
|
||||
ServletContext servletContext = servletContextEvent.getServletContext();
|
||||
bulletinBoard = (BulletinBoardServer) servletContext.getAttribute(BULLETIN_BOARD_ATTRIBUTE_NAME);
|
||||
close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,50 +1,50 @@
|
|||
package meerkat.bulletinboard.webapp;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Message;
|
||||
import meerkat.bulletinboard.service.HelloProtoBuf;
|
||||
import meerkat.protobuf.Crypto.*;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.rest.Constants;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
|
||||
@Path("/proto")
|
||||
public class HelloProtoWebApp {
|
||||
private HelloProtoBuf helloProtoBuf;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
helloProtoBuf = new HelloProtoBuf();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(Constants.MEDIATYPE_PROTOBUF)
|
||||
public Message hello() {
|
||||
byte[] b1 = { (byte) 1, (byte) 2, (byte) 3, (byte) 4 };
|
||||
byte[] b2 = { (byte) 11, (byte) 12, (byte) 13, (byte) 14 };
|
||||
byte[] b3 = {(byte) 21, (byte)22, (byte) 23, (byte) 24};
|
||||
|
||||
Message msg;
|
||||
|
||||
if (helloProtoBuf != null) {
|
||||
msg = helloProtoBuf.sayHello();
|
||||
} else {
|
||||
msg = BulletinBoardMessage.newBuilder()
|
||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||
.addTag("Signature")
|
||||
.addTag("Trustee")
|
||||
.setData(ByteString.copyFrom(b1)).build())
|
||||
.addSig(Signature.newBuilder()
|
||||
.setType(SignatureType.DSA)
|
||||
.setData(ByteString.copyFrom(b2))
|
||||
.setSignerId(ByteString.copyFrom(b3)).build())
|
||||
.build();
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
package meerkat.bulletinboard.webapp;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Message;
|
||||
import meerkat.bulletinboard.service.HelloProtoBuf;
|
||||
import meerkat.protobuf.Crypto.*;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.rest.Constants;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
|
||||
@Path("/proto")
|
||||
public class HelloProtoWebApp {
|
||||
private HelloProtoBuf helloProtoBuf;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
helloProtoBuf = new HelloProtoBuf();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(Constants.MEDIATYPE_PROTOBUF)
|
||||
public Message hello() {
|
||||
byte[] b1 = { (byte) 1, (byte) 2, (byte) 3, (byte) 4 };
|
||||
byte[] b2 = { (byte) 11, (byte) 12, (byte) 13, (byte) 14 };
|
||||
byte[] b3 = {(byte) 21, (byte)22, (byte) 23, (byte) 24};
|
||||
|
||||
Message msg;
|
||||
|
||||
if (helloProtoBuf != null) {
|
||||
msg = helloProtoBuf.sayHello();
|
||||
} else {
|
||||
msg = BulletinBoardMessage.newBuilder()
|
||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||
.addTag("Signature")
|
||||
.addTag("Trustee")
|
||||
.setData(ByteString.copyFrom(b1)).build())
|
||||
.addSig(Signature.newBuilder()
|
||||
.setType(SignatureType.DSA)
|
||||
.setData(ByteString.copyFrom(b2))
|
||||
.setSignerId(ByteString.copyFrom(b3)).build())
|
||||
.build();
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package meerkat;
|
||||
|
||||
option java_package = "meerkat.protobuf";
|
||||
|
||||
message Boolean {
|
||||
bool value = 1;
|
||||
syntax = "proto3";
|
||||
|
||||
package meerkat;
|
||||
|
||||
option java_package = "meerkat.protobuf";
|
||||
|
||||
message Boolean {
|
||||
bool value = 1;
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
|
||||
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||
<Call name="setAttribute">
|
||||
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
|
||||
<Arg>none</Arg>
|
||||
</Call>
|
||||
<Call name="setAttribute">
|
||||
<Arg>org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern</Arg>
|
||||
<Arg>none</Arg>
|
||||
</Call>
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
|
||||
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||
<Call name="setAttribute">
|
||||
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
|
||||
<Arg>none</Arg>
|
||||
</Call>
|
||||
<Call name="setAttribute">
|
||||
<Arg>org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern</Arg>
|
||||
<Arg>none</Arg>
|
||||
</Call>
|
||||
</Configure>
|
|
@ -1,38 +1,38 @@
|
|||
<web-app>
|
||||
<servlet>
|
||||
<servlet-name>Jersey Hello World</servlet-name>
|
||||
<servlet-class>
|
||||
org.glassfish.jersey.servlet.ServletContainer
|
||||
</servlet-class>
|
||||
<init-param>
|
||||
<param-name>jersey.config.server.provider.packages</param-name>
|
||||
<param-value>meerkat</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>Jersey Hello World</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
<context-param>
|
||||
<param-name>dbAddress</param-name>
|
||||
<param-value>localhost</param-value></context-param>
|
||||
<context-param>
|
||||
<param-name>dbPort</param-name>
|
||||
<param-value>3306</param-value></context-param>
|
||||
<context-param>
|
||||
<param-name>dbName</param-name>
|
||||
<param-value>meerkat</param-value></context-param>
|
||||
<context-param>
|
||||
<param-name>username</param-name>
|
||||
<param-value>arbel</param-value></context-param>
|
||||
<context-param>
|
||||
<param-name>password</param-name>
|
||||
<param-value>mypass</param-value></context-param>
|
||||
<context-param>
|
||||
<param-name>dbType</param-name>
|
||||
<param-value>SQLite</param-value></context-param>
|
||||
<listener>
|
||||
<listener-class>meerkat.bulletinboard.webapp.BulletinBoardWebApp</listener-class>
|
||||
</listener>
|
||||
</web-app>
|
||||
<web-app>
|
||||
<servlet>
|
||||
<servlet-name>Jersey Hello World</servlet-name>
|
||||
<servlet-class>
|
||||
org.glassfish.jersey.servlet.ServletContainer
|
||||
</servlet-class>
|
||||
<init-param>
|
||||
<param-name>jersey.config.server.provider.packages</param-name>
|
||||
<param-value>meerkat</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>Jersey Hello World</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
<context-param>
|
||||
<param-name>dbAddress</param-name>
|
||||
<param-value>localhost</param-value></context-param>
|
||||
<context-param>
|
||||
<param-name>dbPort</param-name>
|
||||
<param-value>3306</param-value></context-param>
|
||||
<context-param>
|
||||
<param-name>dbName</param-name>
|
||||
<param-value>meerkat</param-value></context-param>
|
||||
<context-param>
|
||||
<param-name>username</param-name>
|
||||
<param-value>arbel</param-value></context-param>
|
||||
<context-param>
|
||||
<param-name>password</param-name>
|
||||
<param-value>mypass</param-value></context-param>
|
||||
<context-param>
|
||||
<param-name>dbType</param-name>
|
||||
<param-value>H2</param-value></context-param>
|
||||
<listener>
|
||||
<listener-class>meerkat.bulletinboard.webapp.BulletinBoardWebApp</listener-class>
|
||||
</listener>
|
||||
</web-app>
|
||||
|
|
|
@ -1,124 +1,150 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.TextFormat;
|
||||
|
||||
import meerkat.protobuf.Crypto.*;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.rest.Constants;
|
||||
import meerkat.rest.ProtobufMessageBodyReader;
|
||||
import meerkat.rest.ProtobufMessageBodyWriter;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
public class BulletinBoardSQLServerIntegrationTest {
|
||||
|
||||
private static String PROP_GETTY_URL = "gretty.httpBaseURI";
|
||||
private static String DEFAULT_BASE_URL = "http://localhost:8081";
|
||||
private static String BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL);
|
||||
|
||||
Client client;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
System.err.println("Registering client");
|
||||
client = ClientBuilder.newClient();
|
||||
client.register(ProtobufMessageBodyReader.class);
|
||||
client.register(ProtobufMessageBodyWriter.class);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPost() throws Exception {
|
||||
byte[] b1 = {(byte) 1, (byte) 2, (byte) 3, (byte) 4};
|
||||
byte[] b2 = {(byte) 11, (byte) 12, (byte) 13, (byte) 14};
|
||||
byte[] b3 = {(byte) 21, (byte) 22, (byte) 23, (byte) 24};
|
||||
byte[] b4 = {(byte) 4, (byte) 5, (byte) 100, (byte) -50, (byte) 0};
|
||||
|
||||
WebTarget webTarget;
|
||||
Response response;
|
||||
BoolMsg bool;
|
||||
|
||||
BulletinBoardMessage msg;
|
||||
|
||||
MessageFilterList filterList;
|
||||
BulletinBoardMessageList msgList;
|
||||
|
||||
// Test writing mechanism
|
||||
|
||||
System.err.println("******** Testing: " + Constants.POST_MESSAGE_PATH);
|
||||
webTarget = client.target(BASE_URL).path(Constants.BULLETIN_BOARD_SERVER_PATH).path(Constants.POST_MESSAGE_PATH);
|
||||
System.err.println(webTarget.getUri());
|
||||
|
||||
msg = BulletinBoardMessage.newBuilder()
|
||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||
.addTag("Signature")
|
||||
.addTag("Trustee")
|
||||
.setData(ByteString.copyFrom(b1))
|
||||
.build())
|
||||
.addSig(Signature.newBuilder()
|
||||
.setType(SignatureType.DSA)
|
||||
.setData(ByteString.copyFrom(b2))
|
||||
.setSignerId(ByteString.copyFrom(b3))
|
||||
.build())
|
||||
.addSig(Signature.newBuilder()
|
||||
.setType(SignatureType.ECDSA)
|
||||
.setData(ByteString.copyFrom(b3))
|
||||
.setSignerId(ByteString.copyFrom(b2))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
||||
System.err.println(response);
|
||||
bool = response.readEntity(BoolMsg.class);
|
||||
assert bool.getValue();
|
||||
|
||||
msg = BulletinBoardMessage.newBuilder()
|
||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||
.addTag("Vote")
|
||||
.addTag("Trustee")
|
||||
.setData(ByteString.copyFrom(b4))
|
||||
.build())
|
||||
.addSig(Signature.newBuilder()
|
||||
.setType(SignatureType.ECDSA)
|
||||
.setData(ByteString.copyFrom(b4))
|
||||
.setSignerId(ByteString.copyFrom(b2))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
||||
System.err.println(response);
|
||||
bool = response.readEntity(BoolMsg.class);
|
||||
assert bool.getValue();
|
||||
|
||||
// Test reading mechanism
|
||||
|
||||
System.err.println("******** Testing: " + Constants.READ_MESSAGES_PATH);
|
||||
webTarget = client.target(BASE_URL).path(Constants.BULLETIN_BOARD_SERVER_PATH).path(Constants.READ_MESSAGES_PATH);
|
||||
filterList = MessageFilterList.newBuilder()
|
||||
.addFilter(
|
||||
MessageFilter.newBuilder()
|
||||
.setType(FilterType.TAG)
|
||||
.setTag("Vote")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF));
|
||||
System.err.println(response);
|
||||
msgList = response.readEntity(BulletinBoardMessageList.class);
|
||||
System.err.println("List size: " + msgList.getMessageCount());
|
||||
System.err.println("This is the list:");
|
||||
System.err.println(TextFormat.printToString(msgList));
|
||||
assert msgList.getMessageCount() == 1;
|
||||
}
|
||||
|
||||
}
|
||||
package meerkat.bulletinboard;
|
||||
|
||||
|
||||
import com.google.protobuf.BoolValue;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.TextFormat;
|
||||
|
||||
import com.google.protobuf.Timestamp;
|
||||
import meerkat.comm.MessageInputStream;
|
||||
import meerkat.protobuf.Crypto.*;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.protobuf.Comm.*;
|
||||
import static meerkat.bulletinboard.BulletinBoardConstants.*;
|
||||
import meerkat.rest.Constants;
|
||||
import meerkat.rest.ProtobufMessageBodyReader;
|
||||
import meerkat.rest.ProtobufMessageBodyWriter;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
public class BulletinBoardSQLServerIntegrationTest {
|
||||
|
||||
private static String PROP_GETTY_URL = "gretty.httpBaseURI";
|
||||
private static String DEFAULT_BASE_URL = "http://localhost:8081";
|
||||
private static String BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL);
|
||||
|
||||
Client client;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
System.err.println("Registering client");
|
||||
client = ClientBuilder.newClient();
|
||||
client.register(ProtobufMessageBodyReader.class);
|
||||
client.register(ProtobufMessageBodyWriter.class);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPost() throws Exception {
|
||||
byte[] b1 = {(byte) 1, (byte) 2, (byte) 3, (byte) 4};
|
||||
byte[] b2 = {(byte) 11, (byte) 12, (byte) 13, (byte) 14};
|
||||
byte[] b3 = {(byte) 21, (byte) 22, (byte) 23, (byte) 24};
|
||||
byte[] b4 = {(byte) 4, (byte) 5, (byte) 100, (byte) -50, (byte) 0};
|
||||
|
||||
Timestamp t1 = Timestamp.newBuilder()
|
||||
.setSeconds(8276482)
|
||||
.setNanos(4314)
|
||||
.build();
|
||||
|
||||
Timestamp t2 = Timestamp.newBuilder()
|
||||
.setSeconds(987591)
|
||||
.setNanos(1513)
|
||||
.build();
|
||||
|
||||
WebTarget webTarget;
|
||||
Response response;
|
||||
BoolValue bool;
|
||||
|
||||
BulletinBoardMessage msg;
|
||||
|
||||
MessageFilterList filterList;
|
||||
List<BulletinBoardMessage> msgList;
|
||||
|
||||
// Test writing mechanism
|
||||
|
||||
System.err.println("******** Testing: " + POST_MESSAGE_PATH);
|
||||
webTarget = client.target(BASE_URL).path(BULLETIN_BOARD_SERVER_PATH).path(POST_MESSAGE_PATH);
|
||||
System.err.println(webTarget.getUri());
|
||||
|
||||
msg = BulletinBoardMessage.newBuilder()
|
||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||
.addTag("Signature")
|
||||
.addTag("Trustee")
|
||||
.setData(ByteString.copyFrom(b1))
|
||||
.setTimestamp(t1)
|
||||
.build())
|
||||
.addSig(Signature.newBuilder()
|
||||
.setType(SignatureType.DSA)
|
||||
.setData(ByteString.copyFrom(b2))
|
||||
.setSignerId(ByteString.copyFrom(b3))
|
||||
.build())
|
||||
.addSig(Signature.newBuilder()
|
||||
.setType(SignatureType.ECDSA)
|
||||
.setData(ByteString.copyFrom(b3))
|
||||
.setSignerId(ByteString.copyFrom(b2))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
||||
System.err.println(response);
|
||||
bool = response.readEntity(BoolValue.class);
|
||||
assert bool.getValue();
|
||||
|
||||
msg = BulletinBoardMessage.newBuilder()
|
||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||
.addTag("Vote")
|
||||
.addTag("Trustee")
|
||||
.setData(ByteString.copyFrom(b4))
|
||||
.setTimestamp(t2)
|
||||
.build())
|
||||
.addSig(Signature.newBuilder()
|
||||
.setType(SignatureType.ECDSA)
|
||||
.setData(ByteString.copyFrom(b4))
|
||||
.setSignerId(ByteString.copyFrom(b2))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
||||
System.err.println(response);
|
||||
bool = response.readEntity(BoolValue.class);
|
||||
assert bool.getValue();
|
||||
|
||||
// Test reading mechanism
|
||||
|
||||
System.err.println("******** Testing: " + READ_MESSAGES_PATH);
|
||||
webTarget = client.target(BASE_URL).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH);
|
||||
filterList = MessageFilterList.newBuilder()
|
||||
.addFilter(
|
||||
MessageFilter.newBuilder()
|
||||
.setType(FilterType.TAG)
|
||||
.setTag("Vote")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF), InputStream.class);
|
||||
|
||||
MessageInputStream<BulletinBoardMessage> inputStream =
|
||||
MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BulletinBoardMessage.class);
|
||||
|
||||
msgList = inputStream.asList();
|
||||
System.err.println("List size: " + msgList.size());
|
||||
System.err.println("This is the list:");
|
||||
|
||||
for (BulletinBoardMessage message : msgList) {
|
||||
System.err.println(TextFormat.printToString(message));
|
||||
}
|
||||
|
||||
assert msgList.size() == 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,122 +1,165 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider;
|
||||
import meerkat.bulletinboard.sqlserver.H2QueryProvider;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.Result;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.sql.*;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 07-Dec-15.
|
||||
*/
|
||||
public class H2BulletinBoardServerTest {
|
||||
|
||||
private final String dbName = "meerkatTest";
|
||||
|
||||
private GenericBulletinBoardServerTest serverTest;
|
||||
|
||||
private SQLQueryProvider queryProvider;
|
||||
|
||||
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
|
||||
|
||||
@Before
|
||||
public void init(){
|
||||
|
||||
System.err.println("Starting to initialize H2BulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
queryProvider = new H2QueryProvider(dbName);
|
||||
|
||||
try {
|
||||
|
||||
Connection conn = queryProvider.getDataSource().getConnection();
|
||||
Statement stmt = conn.createStatement();
|
||||
|
||||
List<String> deletionQueries = queryProvider.getSchemaDeletionCommands();
|
||||
|
||||
for (String deletionQuery : deletionQueries) {
|
||||
stmt.execute(deletionQuery);
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(queryProvider);
|
||||
try {
|
||||
bulletinBoardServer.init("");
|
||||
|
||||
} catch (CommunicationException e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
serverTest = new GenericBulletinBoardServerTest();
|
||||
try {
|
||||
serverTest.init(bulletinBoardServer);
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished initializing H2BulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bulkTest() {
|
||||
System.err.println("Starting bulkTest of H2BulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
try {
|
||||
serverTest.testInsert();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
serverTest.testSimpleTagAndSignature();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
serverTest.testEnhancedTagsAndSignatures();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished bulkTest of H2BulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
System.err.println("Starting to close H2BulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
serverTest.close();
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished closing H2BulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
}
|
||||
package meerkat.bulletinboard;
|
||||
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider;
|
||||
import meerkat.bulletinboard.sqlserver.H2QueryProvider;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.Result;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.sql.*;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 07-Dec-15.
|
||||
*/
|
||||
public class H2BulletinBoardServerTest {
|
||||
|
||||
private final String dbName = "meerkatTest";
|
||||
|
||||
private GenericBulletinBoardServerTest serverTest;
|
||||
|
||||
private SQLQueryProvider queryProvider;
|
||||
|
||||
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
|
||||
|
||||
@Before
|
||||
public void init(){
|
||||
|
||||
System.err.println("Starting to initialize H2BulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
queryProvider = new H2QueryProvider(dbName);
|
||||
|
||||
try {
|
||||
|
||||
Connection conn = queryProvider.getDataSource().getConnection();
|
||||
Statement stmt = conn.createStatement();
|
||||
|
||||
List<String> deletionQueries = queryProvider.getSchemaDeletionCommands();
|
||||
|
||||
for (String deletionQuery : deletionQueries) {
|
||||
stmt.execute(deletionQuery);
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(queryProvider);
|
||||
try {
|
||||
bulletinBoardServer.init("");
|
||||
|
||||
} catch (CommunicationException e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
serverTest = new GenericBulletinBoardServerTest();
|
||||
try {
|
||||
serverTest.init(bulletinBoardServer);
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished initializing H2BulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bulkTest() {
|
||||
System.err.println("Starting bulkTest of H2BulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
try {
|
||||
serverTest.testInsert();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
serverTest.testSimpleTagAndSignature();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
serverTest.testEnhancedTagsAndSignatures();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished bulkTest of H2BulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchPostAfterClose() {
|
||||
try{
|
||||
serverTest.testBatchPostAfterClose();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatch() {
|
||||
|
||||
final int BATCH_NUM = 20;
|
||||
|
||||
try{
|
||||
for (int i = 0 ; i < BATCH_NUM ; i++) {
|
||||
serverTest.testPostBatch();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
serverTest.testReadBatch();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncQuery() {
|
||||
try {
|
||||
serverTest.testSyncQuery();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
System.err.println("Starting to close H2BulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
serverTest.close();
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished closing H2BulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,42 +1,42 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.rest.Constants;
|
||||
import meerkat.rest.ProtobufMessageBodyReader;
|
||||
import meerkat.rest.ProtobufMessageBodyWriter;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
/**
|
||||
* Created by talm on 10/11/15.
|
||||
*/
|
||||
public class HelloProtoIntegrationTest {
|
||||
private static String PROP_GETTY_URL = "gretty.httpBaseURI";
|
||||
private static String DEFAULT_BASE_URL = "http://localhost:8081/";
|
||||
private static String BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL);
|
||||
private static String HELLO_URL = "proto";
|
||||
|
||||
@Test
|
||||
public void testHello() throws Exception {
|
||||
Client client = ClientBuilder.newClient();
|
||||
client.register(ProtobufMessageBodyReader.class);
|
||||
client.register(ProtobufMessageBodyWriter.class);
|
||||
|
||||
WebTarget webTarget = client.target(BASE_URL).path(HELLO_URL);
|
||||
BulletinBoardMessage response = webTarget.request(Constants.MEDIATYPE_PROTOBUF)
|
||||
.get(BulletinBoardMessage.class);
|
||||
|
||||
System.out.println(response.getMsg().getData());
|
||||
|
||||
assertThat(response.getMsg().getData().toStringUtf8(), is("Hello World!"));
|
||||
assertThat(response.getMsg().getTagCount(), is(2));
|
||||
assertThat(response.getMsg().getTag(0), is("Greetings"));
|
||||
assertThat(response.getMsg().getTag(1), is("FirstPrograms"));
|
||||
}
|
||||
}
|
||||
package meerkat.bulletinboard;
|
||||
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import meerkat.rest.Constants;
|
||||
import meerkat.rest.ProtobufMessageBodyReader;
|
||||
import meerkat.rest.ProtobufMessageBodyWriter;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
/**
|
||||
* Created by talm on 10/11/15.
|
||||
*/
|
||||
public class HelloProtoIntegrationTest {
|
||||
private static String PROP_GETTY_URL = "gretty.httpBaseURI";
|
||||
private static String DEFAULT_BASE_URL = "http://localhost:8081/";
|
||||
private static String BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL);
|
||||
private static String HELLO_URL = "proto";
|
||||
|
||||
@Test
|
||||
public void testHello() throws Exception {
|
||||
Client client = ClientBuilder.newClient();
|
||||
client.register(ProtobufMessageBodyReader.class);
|
||||
client.register(ProtobufMessageBodyWriter.class);
|
||||
|
||||
WebTarget webTarget = client.target(BASE_URL).path(HELLO_URL);
|
||||
BulletinBoardMessage response = webTarget.request(Constants.MEDIATYPE_PROTOBUF)
|
||||
.get(BulletinBoardMessage.class);
|
||||
|
||||
System.out.println(response.getMsg().getData());
|
||||
|
||||
assertThat(response.getMsg().getData().toStringUtf8(), is("Hello World!"));
|
||||
assertThat(response.getMsg().getTagCount(), is(2));
|
||||
assertThat(response.getMsg().getTag(0), is("Greetings"));
|
||||
assertThat(response.getMsg().getTag(1), is("FirstPrograms"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,126 +1,172 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider;
|
||||
import meerkat.bulletinboard.sqlserver.MySQLQueryProvider;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 07-Dec-15.
|
||||
*/
|
||||
public class MySQLBulletinBoardServerTest {
|
||||
|
||||
private final String dbAddress = "localhost";
|
||||
private final int dbPort = 3306;
|
||||
private final String dbName = "meerkat";
|
||||
private final String username = "arbel";
|
||||
private final String password = "mypass";
|
||||
|
||||
private GenericBulletinBoardServerTest serverTest;
|
||||
|
||||
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
|
||||
|
||||
@Before
|
||||
public void init(){
|
||||
|
||||
System.err.println("Starting to initialize MySQLBulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
SQLQueryProvider queryProvider = new MySQLQueryProvider(dbAddress,dbPort,dbName,username,password);
|
||||
|
||||
try {
|
||||
|
||||
Connection conn = queryProvider.getDataSource().getConnection();
|
||||
Statement stmt = conn.createStatement();
|
||||
|
||||
List<String> deletionQueries = queryProvider.getSchemaDeletionCommands();
|
||||
|
||||
for (String deletionQuery : deletionQueries) {
|
||||
stmt.execute(deletionQuery);
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(queryProvider);
|
||||
try {
|
||||
bulletinBoardServer.init("");
|
||||
|
||||
} catch (CommunicationException e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
serverTest = new GenericBulletinBoardServerTest();
|
||||
try {
|
||||
serverTest.init(bulletinBoardServer);
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished initializing MySQLBulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bulkTest() {
|
||||
System.err.println("Starting bulkTest of MySQLBulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
try {
|
||||
serverTest.testInsert();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
serverTest.testSimpleTagAndSignature();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
serverTest.testEnhancedTagsAndSignatures();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished bulkTest of MySQLBulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
System.err.println("Starting to close MySQLBulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
serverTest.close();
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished closing MySQLBulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
}
|
||||
package meerkat.bulletinboard;
|
||||
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider;
|
||||
import meerkat.bulletinboard.sqlserver.MySQLQueryProvider;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.security.SignatureException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 07-Dec-15.
|
||||
*/
|
||||
public class MySQLBulletinBoardServerTest {
|
||||
|
||||
private final String dbAddress = "localhost";
|
||||
private final int dbPort = 3306;
|
||||
private final String dbName = "meerkat";
|
||||
private final String username = "arbel";
|
||||
private final String password = "mypass";
|
||||
|
||||
private GenericBulletinBoardServerTest serverTest;
|
||||
|
||||
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
|
||||
|
||||
@Before
|
||||
public void init(){
|
||||
|
||||
System.err.println("Starting to initialize MySQLBulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
SQLQueryProvider queryProvider = new MySQLQueryProvider(dbAddress,dbPort,dbName,username,password);
|
||||
|
||||
try {
|
||||
|
||||
Connection conn = queryProvider.getDataSource().getConnection();
|
||||
Statement stmt = conn.createStatement();
|
||||
|
||||
List<String> deletionQueries = queryProvider.getSchemaDeletionCommands();
|
||||
|
||||
for (String deletionQuery : deletionQueries) {
|
||||
stmt.execute(deletionQuery);
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(queryProvider);
|
||||
try {
|
||||
bulletinBoardServer.init("");
|
||||
|
||||
} catch (CommunicationException e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
serverTest = new GenericBulletinBoardServerTest();
|
||||
try {
|
||||
serverTest.init(bulletinBoardServer);
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished initializing MySQLBulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bulkTest() {
|
||||
System.err.println("Starting bulkTest of MySQLBulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
try {
|
||||
serverTest.testInsert();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
serverTest.testSimpleTagAndSignature();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
serverTest.testEnhancedTagsAndSignatures();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished bulkTest of MySQLBulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchPostAfterClose() {
|
||||
try{
|
||||
serverTest.testBatchPostAfterClose();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatch() {
|
||||
|
||||
final int BATCH_NUM = 20;
|
||||
|
||||
try{
|
||||
for (int i = 0 ; i < BATCH_NUM ; i++) {
|
||||
serverTest.testPostBatch();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
serverTest.testReadBatch();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncQuery() {
|
||||
try {
|
||||
serverTest.testSyncQuery();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
System.err.println("Starting to close MySQLBulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
serverTest.close();
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished closing MySQLBulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,106 +1,106 @@
|
|||
package meerkat.bulletinboard;
|
||||
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
||||
import meerkat.bulletinboard.sqlserver.SQLiteQueryProvider;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.protobuf.*;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 07-Dec-15.
|
||||
*/
|
||||
public class SQLiteBulletinBoardServerTest{
|
||||
|
||||
private String testFilename = "SQLiteDBTest.db";
|
||||
|
||||
private GenericBulletinBoardServerTest serverTest;
|
||||
|
||||
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
|
||||
|
||||
@Before
|
||||
public void init(){
|
||||
|
||||
System.err.println("Starting to initialize SQLiteBulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
File old = new File(testFilename);
|
||||
old.delete();
|
||||
|
||||
BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(new SQLiteQueryProvider(testFilename));
|
||||
try {
|
||||
bulletinBoardServer.init("");
|
||||
|
||||
} catch (CommunicationException e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
serverTest = new GenericBulletinBoardServerTest();
|
||||
try {
|
||||
serverTest.init(bulletinBoardServer);
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished initializing SQLiteBulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bulkTest() {
|
||||
System.err.println("Starting bulkTest of SQLiteBulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
try {
|
||||
serverTest.testInsert();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
serverTest.testSimpleTagAndSignature();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
serverTest.testEnhancedTagsAndSignatures();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished bulkTest of SQLiteBulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
System.err.println("Starting to close SQLiteBulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
serverTest.close();
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished closing SQLiteBulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
}
|
||||
package meerkat.bulletinboard;
|
||||
|
||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
||||
import meerkat.bulletinboard.sqlserver.SQLiteQueryProvider;
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.protobuf.*;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Created by Arbel Deutsch Peled on 07-Dec-15.
|
||||
*/
|
||||
public class SQLiteBulletinBoardServerTest{
|
||||
|
||||
private String testFilename = "SQLiteDBTest.db";
|
||||
|
||||
private GenericBulletinBoardServerTest serverTest;
|
||||
|
||||
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
|
||||
|
||||
@Before
|
||||
public void init(){
|
||||
|
||||
System.err.println("Starting to initialize SQLiteBulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
File old = new File(testFilename);
|
||||
old.delete();
|
||||
|
||||
BulletinBoardServer bulletinBoardServer = new BulletinBoardSQLServer(new SQLiteQueryProvider(testFilename));
|
||||
try {
|
||||
bulletinBoardServer.init("");
|
||||
|
||||
} catch (CommunicationException e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
serverTest = new GenericBulletinBoardServerTest();
|
||||
try {
|
||||
serverTest.init(bulletinBoardServer);
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished initializing SQLiteBulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bulkTest() {
|
||||
System.err.println("Starting bulkTest of SQLiteBulletinBoardServerTest");
|
||||
long start = threadBean.getCurrentThreadCpuTime();
|
||||
|
||||
try {
|
||||
serverTest.testInsert();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
serverTest.testSimpleTagAndSignature();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
serverTest.testEnhancedTagsAndSignatures();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
long end = threadBean.getCurrentThreadCpuTime();
|
||||
System.err.println("Finished bulkTest of SQLiteBulletinBoardServerTest");
|
||||
System.err.println("Time of operation: " + (end - start));
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
|
||||
plugins {
|
||||
id "us.kirchmeier.capsule" version "1.0.1"
|
||||
id 'com.google.protobuf' version '0.7.0'
|
||||
}
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'idea'
|
||||
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
// Uncomment the lines below to define an application
|
||||
// (this will also allow you to build a "fatCapsule" which includes
|
||||
// the entire application, including all dependencies in a single jar)
|
||||
//apply plugin: 'application'
|
||||
//mainClassName='your.main.ApplicationClass'
|
||||
|
||||
// Is this a snapshot version?
|
||||
ext { isSnapshot = false }
|
||||
|
||||
ext {
|
||||
groupId = 'org.factcenter.meerkat'
|
||||
nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/"
|
||||
|
||||
// Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing)
|
||||
// Should be set in ${HOME}/.gradle/gradle.properties
|
||||
nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : ""
|
||||
nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : ""
|
||||
}
|
||||
|
||||
description = "TODO: Add a description"
|
||||
|
||||
// Your project version
|
||||
version = "0.0"
|
||||
|
||||
version += "${isSnapshot ? '-SNAPSHOT' : ''}"
|
||||
|
||||
|
||||
dependencies {
|
||||
// Meerkat common
|
||||
compile project(':meerkat-common')
|
||||
|
||||
// Logging
|
||||
compile 'org.slf4j:slf4j-api:1.7.7'
|
||||
runtime 'ch.qos.logback:logback-classic:1.1.2'
|
||||
runtime 'ch.qos.logback:logback-core:1.1.2'
|
||||
|
||||
// Google protobufs
|
||||
compile 'com.google.protobuf:protobuf-java:3.+'
|
||||
|
||||
// Depend on test resources from meerkat-common
|
||||
testCompile project(path: ':meerkat-common', configuration: 'testOutput')
|
||||
|
||||
testCompile 'junit:junit:4.+'
|
||||
|
||||
runtime 'org.codehaus.groovy:groovy:2.4.+'
|
||||
}
|
||||
|
||||
|
||||
/*==== You probably don't have to edit below this line =======*/
|
||||
|
||||
// Setup test configuration that can appear as a dependency in
|
||||
// other subprojects
|
||||
configurations {
|
||||
testOutput.extendsFrom (testCompile)
|
||||
}
|
||||
|
||||
task testJar(type: Jar, dependsOn: testClasses) {
|
||||
classifier = 'tests'
|
||||
from sourceSets.test.output
|
||||
}
|
||||
|
||||
artifacts {
|
||||
testOutput testJar
|
||||
}
|
||||
|
||||
// The run task added by the application plugin
|
||||
// is also of type JavaExec.
|
||||
tasks.withType(JavaExec) {
|
||||
// Assign all Java system properties from
|
||||
// the command line to the JavaExec task.
|
||||
systemProperties System.properties
|
||||
}
|
||||
|
||||
|
||||
protobuf {
|
||||
// Configure the protoc executable
|
||||
protoc {
|
||||
// Download from repositories
|
||||
artifact = 'com.google.protobuf:protoc:3.+'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
idea {
|
||||
module {
|
||||
project.sourceSets.each { sourceSet ->
|
||||
|
||||
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
||||
|
||||
println "Adding $srcDir"
|
||||
// add protobuf generated sources to generated source dir.
|
||||
if ("test".equals(sourceSet.name)) {
|
||||
testSourceDirs += file(srcDir)
|
||||
} else {
|
||||
sourceDirs += file(srcDir)
|
||||
}
|
||||
generatedSourceDirs += file(srcDir)
|
||||
|
||||
}
|
||||
|
||||
// Don't exclude build directory
|
||||
excludeDirs -= file(buildDir)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*===================================
|
||||
* "Fat" Build targets
|
||||
*===================================*/
|
||||
|
||||
|
||||
if (project.hasProperty('mainClassName') && (mainClassName != null)) {
|
||||
|
||||
task mavenCapsule(type: MavenCapsule) {
|
||||
description = "Generate a capsule jar that automatically downloads and caches dependencies when run."
|
||||
applicationClass mainClassName
|
||||
destinationDir = buildDir
|
||||
}
|
||||
|
||||
task fatCapsule(type: FatCapsule) {
|
||||
description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class"
|
||||
|
||||
destinationDir = buildDir
|
||||
|
||||
def fatMain = hasProperty('fatmain') ? fatmain : mainClassName
|
||||
|
||||
applicationClass fatMain
|
||||
|
||||
def testJar = hasProperty('test')
|
||||
|
||||
if (hasProperty('fatmain')) {
|
||||
appendix = "fat-${fatMain}"
|
||||
} else {
|
||||
appendix = "fat"
|
||||
}
|
||||
|
||||
if (testJar) {
|
||||
from sourceSets.test.output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*===================================
|
||||
* Repositories
|
||||
*===================================*/
|
||||
|
||||
repositories {
|
||||
|
||||
// Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral)
|
||||
maven {
|
||||
url nexusRepository
|
||||
|
||||
if (isSnapshot) {
|
||||
credentials { username
|
||||
password
|
||||
|
||||
username nexusUser
|
||||
password nexusPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use local maven repository
|
||||
mavenLocal()
|
||||
|
||||
// Use 'maven central' for other dependencies.
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
task "info" << {
|
||||
println "Project: ${project.name}"
|
||||
println "Description: ${project.description}"
|
||||
println "--------------------------"
|
||||
println "GroupId: $groupId"
|
||||
println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})"
|
||||
println ""
|
||||
}
|
||||
info.description 'Print some information about project parameters'
|
||||
|
||||
|
||||
/*===================================
|
||||
* Publishing
|
||||
*===================================*/
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
groupId project.groupId
|
||||
pom.withXml {
|
||||
asNode().appendNode('description', project.description)
|
||||
}
|
||||
from project.components.java
|
||||
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}"
|
||||
credentials { username
|
||||
password
|
||||
|
||||
username nexusUser
|
||||
password nexusPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package meerkat.crypto.dkg.comm;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import meerkat.comm.Channel;
|
||||
import meerkat.protobuf.Comm;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 2/14/2016.
|
||||
*
|
||||
* an implementation of ReceiverCallback
|
||||
*/
|
||||
public abstract class MessageHandler implements Channel.ReceiverCallback {
|
||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* fixed value for broadcasting
|
||||
*/
|
||||
public static final int BROADCAST = 0;
|
||||
|
||||
/**
|
||||
* Handle a broadcast (or unicast) message.
|
||||
* If the message is invalid, the handler can throw an {@link InvalidProtocolBufferException}, in which
|
||||
* case the message will simply be ignored.
|
||||
* @param envelope
|
||||
*/
|
||||
public abstract void handleMessage(Comm.BroadcastMessage envelope) throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Was this broadcastMessage was received by broadcast channel
|
||||
* @param broadcastMessage
|
||||
* @return broadcastMessage user destination == BROADCAST
|
||||
*/
|
||||
public boolean isBroadcast(Comm.BroadcastMessage broadcastMessage){
|
||||
return broadcastMessage.getDestination() == BROADCAST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveMessage(Comm.BroadcastMessage envelope) {
|
||||
try {
|
||||
handleMessage(envelope);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
logger.warn("Received invalid protocol buffer from channel", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package meerkat.crypto.dkg.comm;
|
||||
|
||||
import meerkat.protobuf.DKG;
|
||||
|
||||
/**
|
||||
* Created by talm on 12/04/16.
|
||||
*/
|
||||
public class MessageUtils {
|
||||
public static DKG.Payload createMessage(DKG.Payload.Type type) {
|
||||
return DKG.Payload.newBuilder().setType(type).build();
|
||||
}
|
||||
|
||||
public static DKG.Payload createMessage(DKG.Payload.Type type, DKG.ShareMessage share) {
|
||||
return DKG.Payload.newBuilder().setType(type).setShare(share).build();
|
||||
}
|
||||
|
||||
public static DKG.Payload createMessage(DKG.Payload.Type type, DKG.ShareMessage.Builder share) {
|
||||
return DKG.Payload.newBuilder().setType(type).setShare(share).build();
|
||||
}
|
||||
|
||||
public static DKG.Payload createMessage(DKG.Payload.Type type, DKG.IDMessage id) {
|
||||
return DKG.Payload.newBuilder().setType(type).setId(id).build();
|
||||
}
|
||||
|
||||
public static DKG.Payload createMessage(DKG.Payload.Type type, DKG.IDMessage.Builder id) {
|
||||
return DKG.Payload.newBuilder().setType(type).setId(id).build();
|
||||
}
|
||||
|
||||
public static DKG.Payload createMessage(DKG.Payload.Type type, DKG.CommitmentMessage commitment) {
|
||||
return DKG.Payload.newBuilder().setType(type).setCommitment(commitment).build();
|
||||
}
|
||||
|
||||
public static DKG.Payload createMessage(DKG.Payload.Type type, DKG.CommitmentMessage.Builder commitment) {
|
||||
return DKG.Payload.newBuilder().setType(type).setCommitment(commitment).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package meerkat.crypto.dkg.feldman;
|
||||
|
||||
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 3/14/2016.
|
||||
*
|
||||
* contains all relevant information on specific party during
|
||||
* the run of Joint Feldamn protocol
|
||||
*/
|
||||
public class Party<T> {
|
||||
public final int id;
|
||||
public Polynomial.Point share;
|
||||
public ArrayList<T> commitments;
|
||||
public boolean doneFlag;
|
||||
public Protocol.ComplaintState[] complaints;
|
||||
public boolean aborted;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param id party identifier - 1 <= id <= n
|
||||
* @param n number of parties in current run protocol
|
||||
* @param t protocol's threshold
|
||||
*/
|
||||
public Party(int id, int n, int t) {
|
||||
this.id = id;
|
||||
this.share = null;
|
||||
this.doneFlag = false;
|
||||
this.complaints = new Protocol.ComplaintState[n];
|
||||
Arrays.fill(this.complaints, Protocol.ComplaintState.OK);
|
||||
this.commitments = new ArrayList<T>(t + 1);
|
||||
for (int i = 0; i <= t ; i++){
|
||||
commitments.add(null);
|
||||
}
|
||||
this.aborted = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,368 @@
|
|||
package meerkat.crypto.dkg.feldman;
|
||||
|
||||
import meerkat.comm.Channel;
|
||||
import meerkat.crypto.secretsharing.feldman.VerifiableSecretSharing;
|
||||
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.protobuf.DKG;
|
||||
import org.factcenter.qilin.primitives.Group;
|
||||
import org.factcenter.qilin.util.ByteEncoder;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
import static meerkat.crypto.dkg.comm.MessageUtils.*;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 3/14/2016.
|
||||
*
|
||||
* an implementation of JointFeldman distributed key generation protocol.
|
||||
*
|
||||
* allows set of n parties to generate random key with threshold t.
|
||||
*/
|
||||
public class Protocol<T> extends VerifiableSecretSharing<T> {
|
||||
public enum ComplaintState {
|
||||
/**
|
||||
* No complaints, no response required at this point.
|
||||
*/
|
||||
OK,
|
||||
|
||||
/**
|
||||
* Party received complaint, waiting for response from party
|
||||
*/
|
||||
Waiting,
|
||||
|
||||
/**
|
||||
* Party gave invalid answer to conplaint.
|
||||
*/
|
||||
Disqualified,
|
||||
|
||||
/**
|
||||
* Party received complaint, gave valid answer.
|
||||
*/
|
||||
NonDisqualified
|
||||
}
|
||||
|
||||
/**
|
||||
* My share id.
|
||||
*/
|
||||
protected final int id;
|
||||
|
||||
/**
|
||||
* All parties participating in key generation.
|
||||
* parties[id-1] has my info.
|
||||
*/
|
||||
private Party<T>[] parties;
|
||||
|
||||
/**
|
||||
* communication object
|
||||
*/
|
||||
protected Channel channel;
|
||||
|
||||
/**
|
||||
* Encode/Decode group elements
|
||||
*/
|
||||
protected final ByteEncoder<T> encoder;
|
||||
|
||||
/**
|
||||
* constructor
|
||||
* @param q a large prime.
|
||||
* @param t threshold. Any t+1 share holders can recover the secret,
|
||||
* but any set of at most t share holders cannot
|
||||
* @param n number of share holders
|
||||
* @param zi secret, chosen from Zq
|
||||
* @param random use for generate random polynomial
|
||||
* @param group
|
||||
* @param q a large prime dividing group order.
|
||||
* @param g a generator of cyclic group of order q.
|
||||
* the generated group is a subgroup of the given group.
|
||||
* it must be chosen such that computing discrete logarithms is hard in this group.
|
||||
* @param encoder Encode/Decode group elements (of type T) to/from byte array
|
||||
*/
|
||||
public Protocol(int t, int n, BigInteger zi, Random random, BigInteger q, T g
|
||||
, Group<T> group, int id, ByteEncoder<T> encoder) {
|
||||
super(t, n, zi, random, q, g,group);
|
||||
this.id = id;
|
||||
this.parties = new Party[n];
|
||||
for (int i = 1; i <= n ; i++){
|
||||
this.parties[i - 1] = new Party(i,n,t);
|
||||
}
|
||||
this.parties[id - 1].share = getShare(id);
|
||||
this.encoder = encoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter
|
||||
* @param channel
|
||||
*/
|
||||
public void setChannel(Channel channel){
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter
|
||||
* @param parties
|
||||
*/
|
||||
protected void setParties(Party[] parties){
|
||||
this.parties = parties;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return
|
||||
*/
|
||||
protected Party[] getParties(){
|
||||
return parties;
|
||||
}
|
||||
|
||||
/**
|
||||
* stage1.1 according to the protocol
|
||||
* Pi broadcasts Aik for k = 0,...,t.
|
||||
*/
|
||||
public void broadcastCommitments(){
|
||||
broadcastCommitments(commitmentsArrayList);
|
||||
}
|
||||
|
||||
/**
|
||||
* pack commitments as messages and broadcast them
|
||||
* @param commitments
|
||||
*/
|
||||
public void broadcastCommitments(ArrayList<T> commitments){
|
||||
DKG.CommitmentMessage commitmentMessage;
|
||||
for (int k = 0; k <= t ; k++){
|
||||
commitmentMessage = DKG.CommitmentMessage.newBuilder()
|
||||
.setCommitment(ByteString.copyFrom(encoder.encode(commitments.get(k))))
|
||||
.setK(k)
|
||||
.build();
|
||||
channel.broadcastMessage(createMessage(DKG.Payload.Type.COMMITMENT, commitmentMessage));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send channel j her secret share (of my polynomial)
|
||||
* @param j
|
||||
*/
|
||||
public void sendSecret(int j){
|
||||
ByteString secret = ByteString.copyFrom(getShare(j).y.toByteArray());
|
||||
channel.sendMessage(j, createMessage(DKG.Payload.Type.SHARE,
|
||||
DKG.ShareMessage.newBuilder()
|
||||
.setI(id)
|
||||
.setJ(j)
|
||||
.setShare(secret)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* stage1.2 according to the protocol
|
||||
* Pi computes the shares Sij for j = 1,...,n and sends Sij secretly to Pj.
|
||||
*/
|
||||
public void sendSecrets(){
|
||||
for (int j = 1; j <= n ; j++){
|
||||
if(j != id){
|
||||
sendSecret(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param i
|
||||
* @return computeVerificationValue(j,parties[i - 1].commitments,group) == g ^ parties[i - 1].share mod q
|
||||
*/
|
||||
public boolean isValidShare(int i){
|
||||
Party<T> party = parties[i - 1];
|
||||
synchronized (parties[i - 1]) {
|
||||
return isValidShare(party.share, party.commitments, id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param share
|
||||
* @param commitments
|
||||
* @param j
|
||||
* @return computeVerificationValue(j,commitments,group) == g ^ secret.y mod q
|
||||
*/
|
||||
public boolean isValidShare(Polynomial.Point share, ArrayList<T> commitments, int j){
|
||||
try{
|
||||
T v = computeVerificationValue(j,commitments,group);
|
||||
return group.multiply(g,share.y).equals(v);
|
||||
}
|
||||
catch (NullPointerException e){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* stage2 according to the protocol
|
||||
* Pj verifies all the shares he received (using isValidShare)
|
||||
* if check fails for an index i, Pj broadcasts a complaint against Pi.
|
||||
*/
|
||||
public void broadcastComplaints(){
|
||||
for (int i = 1; i <= n ; i++ ){
|
||||
if(i != id && !isValidShare(i)) {
|
||||
broadcastComplaint(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create a complaint message against i and broadcast it
|
||||
* @param i
|
||||
*/
|
||||
private void broadcastComplaint(int i){
|
||||
//message = new Message(Type.Complaint, j)
|
||||
DKG.IDMessage complaint = DKG.IDMessage.newBuilder()
|
||||
.setId(i)
|
||||
.build();
|
||||
channel.broadcastMessage(createMessage(DKG.Payload.Type.COMPLAINT, complaint));
|
||||
}
|
||||
|
||||
/**
|
||||
* create an answer message for j and broadcast it
|
||||
* @param j
|
||||
*/
|
||||
public void broadcastComplaintAnswer(int j){
|
||||
channel.broadcastMessage(createMessage(DKG.Payload.Type.ANSWER, DKG.ShareMessage.newBuilder()
|
||||
.setI(id)
|
||||
.setJ(j)
|
||||
.setShare(ByteString.copyFrom(getShare(j).y.toByteArray()))));
|
||||
}
|
||||
|
||||
/**
|
||||
* stage3.1 according to the protocol
|
||||
* if more than t players complain against a player Pi he is disqualified.
|
||||
*/
|
||||
public void answerAllComplainingPlayers(){
|
||||
ComplaintState[] complaints = parties[id - 1].complaints;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
switch (complaints[i - 1]) {
|
||||
case Waiting:
|
||||
broadcastComplaintAnswer(i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* stage3.2 according to the protocol
|
||||
* if any of the revealed shares fails the verification test, player Pi is disqualified.
|
||||
* set QUAL to be the set of non-disqualified players.
|
||||
*/
|
||||
public Set<Integer> calcQUAL(){
|
||||
Set<Integer> QUAL = new HashSet<Integer>();
|
||||
boolean nonDisqualified;
|
||||
int counter;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
synchronized (parties[i - 1]) {
|
||||
ComplaintState[] complaints = parties[i - 1].complaints;
|
||||
nonDisqualified = true;
|
||||
counter = 0;
|
||||
for (int j = 1; j <= n; j++) {
|
||||
switch (complaints[j - 1]) {
|
||||
case OK:
|
||||
break;
|
||||
case NonDisqualified:
|
||||
counter++;
|
||||
break;
|
||||
default:
|
||||
nonDisqualified = false;
|
||||
break;
|
||||
}
|
||||
if (!nonDisqualified)
|
||||
break;
|
||||
}
|
||||
if (nonDisqualified && counter <= t) {
|
||||
QUAL.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return QUAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* compute Y, the commitment to the final public key (includes only qualifying set)
|
||||
* stage4.1 according to the protocol
|
||||
* public value y is computed as y = multiplication of yi mod p for i in QUAL
|
||||
*/
|
||||
public T calcY(Set<Integer> QUAL){
|
||||
T y = group.zero();
|
||||
for (int i : QUAL) {
|
||||
synchronized (parties[i - 1]) {
|
||||
y = group.add(y, parties[i - 1].commitments.get(0));
|
||||
}
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* stage4.2 according to the protocol
|
||||
* public verification values are computed as Ak = multiplication
|
||||
* of Aik mod p for i in QUAL for k = 0,...,t
|
||||
*/
|
||||
public ArrayList<T> calcCommitments(Set<Integer> QUAL){
|
||||
ArrayList<T> commitments = new ArrayList<T>(t+1);
|
||||
T value;
|
||||
for (int k = 0; k <= t; k++){
|
||||
value = group.zero();
|
||||
for (int i : QUAL) {
|
||||
synchronized (parties[i - 1]) {
|
||||
value = group.add(value, parties[i - 1].commitments.get(k));
|
||||
}
|
||||
}
|
||||
commitments.add(k,value);
|
||||
}
|
||||
return commitments;
|
||||
}
|
||||
|
||||
/**
|
||||
* stage4.3 according to the protocol
|
||||
* Pj sets is share of the share as xj = sum of Sij mod q for i in QUAL
|
||||
*/
|
||||
public Polynomial.Point calcShare(Set<Integer> QUAL){
|
||||
BigInteger xj = BigInteger.ZERO;
|
||||
for (int i : QUAL) {
|
||||
synchronized (parties[i - 1]) {
|
||||
xj = xj.add(parties[i - 1].share.y);
|
||||
}
|
||||
}
|
||||
return new Polynomial.Point(BigInteger.valueOf(id) , xj.mod(q));
|
||||
}
|
||||
|
||||
/**
|
||||
* decode commitment from arr
|
||||
* @param arr
|
||||
* @return
|
||||
*/
|
||||
public T decodeCommitment(byte[] arr){
|
||||
return encoder.decode(arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return id
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return channel
|
||||
*/
|
||||
public Channel getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return encoder
|
||||
*/
|
||||
public ByteEncoder<T> getEncoder() {
|
||||
return encoder;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,588 @@
|
|||
package meerkat.crypto.dkg.feldman;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.TextFormat;
|
||||
import meerkat.comm.Channel;
|
||||
import meerkat.crypto.dkg.comm.MessageHandler;
|
||||
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.protobuf.Comm;
|
||||
import meerkat.protobuf.DKG;
|
||||
import org.factcenter.qilin.primitives.Group;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
|
||||
import static meerkat.crypto.dkg.comm.MessageUtils.createMessage;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 3/14/2016.
|
||||
*
|
||||
* implementation of joint feldman protocol user.
|
||||
*
|
||||
* according to the protocol, each user run feldman verifiable secret sharing
|
||||
* as a dealer.
|
||||
*
|
||||
* by the end of run(), each party in QUAL has his own share of the generated random key.
|
||||
* this key can be recover by any subset of QUAL of size at least t + 1.
|
||||
*/
|
||||
public class User<T> implements Runnable {
|
||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* joint feldman protocol object
|
||||
*/
|
||||
protected final Protocol<T> dkg;
|
||||
|
||||
/**
|
||||
* a generator of cyclic group of order q.
|
||||
* the generated group is a subgroup of the given group.
|
||||
* it must be chosen such that computing discrete logarithms is hard in this group.
|
||||
*/
|
||||
protected final T g;
|
||||
|
||||
/**
|
||||
* cyclic group contains g.
|
||||
*/
|
||||
protected final Group<T> group;
|
||||
|
||||
/**
|
||||
* user id
|
||||
*/
|
||||
protected final int id;
|
||||
|
||||
/**
|
||||
* threshold
|
||||
*/
|
||||
protected final int t;
|
||||
|
||||
/**
|
||||
* number of shares
|
||||
*/
|
||||
protected final int n;
|
||||
|
||||
|
||||
/**
|
||||
* channel object
|
||||
*/
|
||||
protected final Channel channel; //
|
||||
|
||||
/**
|
||||
* All parties participating in key generation.
|
||||
* parties[id-1] has my info.
|
||||
*/
|
||||
protected final Party[] parties;
|
||||
|
||||
/**
|
||||
* set of all non-disqualified parties
|
||||
*/
|
||||
protected Set<Integer> QUAL;
|
||||
|
||||
/**
|
||||
* my own share of the generated random key.
|
||||
*/
|
||||
protected Polynomial.Point share;
|
||||
|
||||
/**
|
||||
* public verification values
|
||||
*/
|
||||
protected ArrayList<T> commitments;
|
||||
|
||||
/**
|
||||
* public value,
|
||||
* y = g ^ key
|
||||
*/
|
||||
protected T y;
|
||||
|
||||
protected BlockingQueue<Comm.BroadcastMessage> receiveQueue;
|
||||
|
||||
/**
|
||||
* constructor
|
||||
* @param dkg joint feldman protocol object
|
||||
* @param channel channel object
|
||||
*/
|
||||
public User(Protocol<T> dkg, Channel channel) {
|
||||
this.dkg = dkg;
|
||||
|
||||
this.g = dkg.getGenerator();
|
||||
this.group = dkg.getGroup();
|
||||
this.n = dkg.getN();
|
||||
this.t = dkg.getT();
|
||||
this.id = dkg.getId();
|
||||
|
||||
this.channel = channel;
|
||||
dkg.setChannel(channel);
|
||||
registerReceiverCallback();
|
||||
|
||||
this.parties = dkg.getParties();
|
||||
this.QUAL = null;
|
||||
this.commitments = null;
|
||||
this.share = null;
|
||||
this.y = null;
|
||||
|
||||
this.receiveQueue = new LinkedBlockingDeque<>();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* create MessageHandler and register it as ReceiverCallback
|
||||
*/
|
||||
protected void registerReceiverCallback() {
|
||||
channel.registerReceiverCallback(new meerkat.crypto.dkg.comm.MessageHandler() {
|
||||
@Override
|
||||
public void handleMessage(Comm.BroadcastMessage envelope) throws InvalidProtocolBufferException {
|
||||
receiveQueue.add(envelope);
|
||||
}
|
||||
});
|
||||
|
||||
// this.messageHandler = new MessageHandler();
|
||||
// channel.registerReceiverCallback(messageHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for at least one message to arrive, then handle any messages currently in the queue
|
||||
*/
|
||||
protected void waitAndHandleReceivedMessages() {
|
||||
Comm.BroadcastMessage msg = null;
|
||||
while (!stop && msg == null) {
|
||||
try {
|
||||
msg = receiveQueue.take();
|
||||
} catch (InterruptedException e) {
|
||||
// Possibly stop
|
||||
}
|
||||
}
|
||||
while (!stop && msg != null) {
|
||||
try {
|
||||
handleMessage(msg);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
logger.warn("Received invalid message: {}", TextFormat.printToString(msg));
|
||||
}
|
||||
msg = receiveQueue.poll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* stage1 according to the protocol
|
||||
* 1. Pi broadcasts Aik for k = 0,...,t.
|
||||
* 2. Pi computes the shares Sij for j = 1,...,n and sends Sij secretly to Pj.
|
||||
*/
|
||||
protected void stage1() {
|
||||
dkg.broadcastCommitments();
|
||||
dkg.sendSecrets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all shares and commitments have arrived from other parties
|
||||
*/
|
||||
protected boolean isStageOneCompleted() {
|
||||
for (int i = 0 ; i < n ; i++) {
|
||||
if (!parties[i].aborted) {
|
||||
if (parties[i].share == null)
|
||||
return false;
|
||||
for (int k = 0 ; k <= t ; k++) {
|
||||
if (parties[i].commitments.get(k) == null)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void waitUntilStageOneCompleted() {
|
||||
while (!stop && !isStageOneCompleted())
|
||||
waitAndHandleReceivedMessages();
|
||||
}
|
||||
|
||||
protected boolean isStageTwoCompleted() {
|
||||
for (int i = 0 ; i < n ; i++) {
|
||||
if (!parties[i].aborted && !parties[i].doneFlag)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* stage2 according to the protocol
|
||||
* Pj verifies all the shares he received
|
||||
* if check fails for an index i, Pj broadcasts a complaint against Pi.
|
||||
* Pj broadcasts done message at the end of this stage
|
||||
*/
|
||||
protected void stage2() {
|
||||
dkg.broadcastComplaints();
|
||||
//broadcast done message after all complaints
|
||||
channel.broadcastMessage(createMessage(DKG.Payload.Type.DONE));
|
||||
}
|
||||
|
||||
/**
|
||||
* wait until all other parties done complaining by receiving done message
|
||||
*/
|
||||
protected void waitUntilStageTwoCompleted(){
|
||||
while (!stop && !isStageTwoCompleted())
|
||||
waitAndHandleReceivedMessages();
|
||||
}
|
||||
|
||||
|
||||
protected boolean haveReceivedAllStage3ComplaintAnswers() {
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (parties[i].aborted)
|
||||
continue;
|
||||
for (int j = 0; j < n; j++) {
|
||||
if (parties[i].complaints[j].equals(Protocol.ComplaintState.Waiting))
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* stage3 according to the protocol
|
||||
* 1. if more than t players complain against a player Pi he is disqualified.
|
||||
* otherwise Pi broadcasts the share Sij for each complaining player Pj.
|
||||
* 2. if any of the revealed shares fails the verification test, player Pi is disqualified.
|
||||
* set QUAL to be the set of non-disqualified players.
|
||||
*/
|
||||
protected void stage3(){
|
||||
dkg.answerAllComplainingPlayers();
|
||||
|
||||
// wait until there is no complaint waiting for answer
|
||||
while (!stop && !haveReceivedAllStage3ComplaintAnswers())
|
||||
waitAndHandleReceivedMessages();
|
||||
|
||||
this.QUAL = dkg.calcQUAL();
|
||||
}
|
||||
|
||||
/**
|
||||
* stage4 according to the protocol
|
||||
* 1. public value y is computed as y = multiplication of yi mod p for i in QUAL
|
||||
* 2. public verification values are computed as Ak = multiplication of Aik mod p for i in QUAL for k = 0,...,t
|
||||
* 3. Pj sets is share of the secret as xj = sum of Sij mod q for i in QUAL
|
||||
*/
|
||||
protected void stage4(){
|
||||
this.y = dkg.calcY(QUAL);
|
||||
this.commitments = dkg.calcCommitments(QUAL);
|
||||
this.share = dkg.calcShare(QUAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.runThread = Thread.currentThread();
|
||||
// For debugging
|
||||
String previousName = runThread.getName();
|
||||
runThread.setName(getClass().getName() +":" + getID());
|
||||
|
||||
try {
|
||||
stage1();
|
||||
waitUntilStageOneCompleted();
|
||||
if (stop) return;
|
||||
stage2();
|
||||
waitUntilStageTwoCompleted();
|
||||
if (stop) return;
|
||||
stage3();
|
||||
if (stop) return;
|
||||
stage4();
|
||||
} finally {
|
||||
runThread.setName(previousName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* current thread in the main loop
|
||||
*/
|
||||
protected Thread runThread;
|
||||
|
||||
/**
|
||||
* flag indicates if there was request to stop the current run of the protocol
|
||||
*/
|
||||
protected boolean stop = false;
|
||||
|
||||
/**
|
||||
* Request the current run loop to exit gracefully
|
||||
*/
|
||||
public void stop() {
|
||||
try {
|
||||
stop = true;
|
||||
runThread.interrupt();
|
||||
}catch (Exception e){
|
||||
//do nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return commitments
|
||||
*/
|
||||
public ArrayList<T> getCommitments() {
|
||||
return commitments;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return g
|
||||
*/
|
||||
public T getGenerator() {
|
||||
return g;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return group
|
||||
*/
|
||||
public Group<T> getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return share
|
||||
*/
|
||||
public Polynomial.Point getShare() {
|
||||
return share;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return id
|
||||
*/
|
||||
public int getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return n
|
||||
*/
|
||||
public int getN() {
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return t
|
||||
*/
|
||||
public int getT() {
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return y
|
||||
*/
|
||||
public T getPublicValue() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return QUAL
|
||||
*/
|
||||
public Set<Integer> getQUAL() {
|
||||
return QUAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return channel
|
||||
*/
|
||||
public Channel getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* commitment message is valid if:
|
||||
* 1. it was received in broadcast chanel
|
||||
* 2. the sender didn't sent this commitment before
|
||||
*/
|
||||
protected boolean isValidCommitmentMessage(int sender, boolean isBroadcast, DKG.CommitmentMessage commitmentMessage){
|
||||
int i = sender - 1;
|
||||
int k = commitmentMessage.getK();
|
||||
return isBroadcast && parties[i].commitments.get(k) == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* secret message is valid if:
|
||||
* 1. it was received in private chanel
|
||||
* 2. the sender didn't sent secret message before
|
||||
* 3. secret.i == i
|
||||
* 4. secret.j == id
|
||||
*/
|
||||
protected boolean isValidSecretMessage(int sender, boolean isBroadcast, DKG.ShareMessage secretMessage){
|
||||
int i = secretMessage.getI();
|
||||
int j = secretMessage.getJ();
|
||||
if(sender != i || isBroadcast)
|
||||
return false;
|
||||
else
|
||||
return parties[i - 1].share == null && j == id;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* done message is valid if:
|
||||
* 1. it was received in broadcast chanel
|
||||
* 2. the sender didn't sent done message before
|
||||
*/
|
||||
protected boolean isValidDoneMessage(int sender, boolean isBroadcast){
|
||||
return isBroadcast && !parties[sender - 1].doneFlag;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* complaint message is valid if:
|
||||
* 1. it was received in broadcast chanel
|
||||
* 2. the sender didn't complained against id before
|
||||
*/
|
||||
protected boolean isValidComplaintMessage(int sender, boolean isBroadcast, DKG.IDMessage complaintMessage){
|
||||
int i = sender;
|
||||
int j = complaintMessage.getId();
|
||||
|
||||
assert(i > 0);
|
||||
assert(j > 0);
|
||||
assert(i <= parties.length);
|
||||
assert(j <= parties[i-1].complaints.length);
|
||||
|
||||
return isBroadcast && parties[i - 1].complaints[j - 1].equals( Protocol.ComplaintState.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* answer message is valid if:
|
||||
* 1. it was received in broadcast chanel
|
||||
* 2. secret.i == i
|
||||
* 3. 1 <= secret.j <= n
|
||||
* 4. it is marked that j complained against i and i didn't received
|
||||
*/
|
||||
protected boolean isValidAnswerMessage(int sender, boolean isBroadcast, DKG.ShareMessage secretMessage){
|
||||
int i = secretMessage.getI();
|
||||
int j = secretMessage.getJ();
|
||||
if(sender != i || !isBroadcast)
|
||||
return false;
|
||||
else
|
||||
return j >= 1 && j <= n && parties[i - 1].complaints[j - 1].equals(Protocol.ComplaintState.Waiting);
|
||||
}
|
||||
|
||||
|
||||
public void handleMessage(Comm.BroadcastMessage envelope) throws InvalidProtocolBufferException {
|
||||
int sender = envelope.getSender();
|
||||
boolean isBroadcast = !envelope.getIsPrivate();
|
||||
DKG.Payload msg = DKG.Payload.parseFrom(envelope.getPayload());
|
||||
|
||||
logger.debug("handling Message: Dst={}, Src={}, [{}]",
|
||||
envelope.getDestination(), envelope.getSender(), TextFormat.printToString(msg));
|
||||
|
||||
switch (msg.getType()) {
|
||||
case COMMITMENT:
|
||||
/**
|
||||
* saves the commitment
|
||||
*/
|
||||
assert msg.getPayloadDataCase() == DKG.Payload.PayloadDataCase.COMMITMENT;
|
||||
DKG.CommitmentMessage commitmentMessage = msg.getCommitment();
|
||||
if (isValidCommitmentMessage(sender, isBroadcast, commitmentMessage)) {
|
||||
int i = sender - 1;
|
||||
int k = commitmentMessage.getK();
|
||||
|
||||
parties[i].commitments.set(k, extractCommitment(commitmentMessage));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case SHARE:
|
||||
/**
|
||||
* saves the secret
|
||||
*/
|
||||
assert msg.getPayloadDataCase() == DKG.Payload.PayloadDataCase.SHARE;
|
||||
DKG.ShareMessage secretMessage = msg.getShare();
|
||||
if(isValidSecretMessage(sender,isBroadcast,secretMessage)) {
|
||||
int i = secretMessage.getI();
|
||||
Polynomial.Point secret = extractShare(id,secretMessage.getShare());
|
||||
parties[i - 1].share = secret;
|
||||
}
|
||||
break;
|
||||
|
||||
case DONE:
|
||||
|
||||
/**
|
||||
* marks that the sender was finished sending all his complaints
|
||||
*/
|
||||
if(isValidDoneMessage(sender,isBroadcast)) {
|
||||
parties[sender - 1].doneFlag = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case COMPLAINT:
|
||||
/**
|
||||
* marks that the sender was complained against id
|
||||
*/
|
||||
if (msg.getPayloadDataCase() != DKG.Payload.PayloadDataCase.ID) {
|
||||
logger.error("User {} Expecting ID message, got from SRC={} msg {}", getID(), envelope.getSender(), TextFormat.printToString(msg));
|
||||
assert (msg.getPayloadDataCase() == DKG.Payload.PayloadDataCase.ID);
|
||||
}
|
||||
|
||||
DKG.IDMessage complaintMessage = msg.getId();
|
||||
if(isValidComplaintMessage(sender,isBroadcast,complaintMessage)){
|
||||
int i = sender;
|
||||
int j = complaintMessage.getId();
|
||||
parties[j - 1].complaints[i - 1] = Protocol.ComplaintState.Waiting;
|
||||
}
|
||||
break;
|
||||
case ANSWER:
|
||||
/**
|
||||
* if the secret is valid, marks the complaint as NonDisqualified
|
||||
* else marks it as Disqualified
|
||||
* in case that the complainer is id ( j == id ), saves the secret
|
||||
*/
|
||||
assert msg.getPayloadDataCase() == DKG.Payload.PayloadDataCase.SHARE;
|
||||
secretMessage = msg.getShare();
|
||||
if(isValidAnswerMessage(sender,isBroadcast,secretMessage)) {
|
||||
int i = secretMessage.getI();
|
||||
int j = secretMessage.getJ();
|
||||
Polynomial.Point secret = extractShare(j,secretMessage.getShare());
|
||||
if (dkg.isValidShare(secret, parties[i - 1].commitments, j)) {
|
||||
parties[i - 1].complaints[j - 1] = Protocol.ComplaintState.NonDisqualified;
|
||||
} else {
|
||||
parties[i - 1].complaints[j - 1] = Protocol.ComplaintState.Disqualified;
|
||||
}
|
||||
if (j == id) {
|
||||
parties[i - 1].share = secret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ABORT:
|
||||
/**
|
||||
* marks that the sender was aborted
|
||||
*/
|
||||
parties[sender - 1].aborted = true;
|
||||
break;
|
||||
default:
|
||||
logger.error("Bad message: SRC={}, DST={}, Payload={}", envelope.getSender(), envelope.getDestination(), TextFormat.printToString(msg));
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* extract share value from ByteString
|
||||
* @param i
|
||||
* @param share
|
||||
* @return new Point (i,share)
|
||||
*/
|
||||
public Polynomial.Point extractShare(int i, ByteString share){
|
||||
BigInteger x = BigInteger.valueOf(i);
|
||||
BigInteger y = new BigInteger(share.toByteArray());
|
||||
return new Polynomial.Point(x,y);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param commitmentMessage
|
||||
* @return
|
||||
*/
|
||||
public T extractCommitment(DKG.CommitmentMessage commitmentMessage){
|
||||
return dkg.decodeCommitment(commitmentMessage.getCommitment().toByteArray());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package meerkat.crypto.dkg.gjkr;
|
||||
|
||||
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 3/16/2016.
|
||||
*
|
||||
* an extension of DistributedKeyGenerationParty
|
||||
* contains all relevant information on specific party during
|
||||
* the run of the safe protocol
|
||||
*/
|
||||
public class Party<T> extends meerkat.crypto.dkg.feldman.Party<T> {
|
||||
public Polynomial.Point shareT;
|
||||
public boolean ysDoneFlag;
|
||||
public ArrayList<T> verifiableValues;
|
||||
public Set<Polynomial.Point> recoverSharesSet;
|
||||
public Party(int id, int n, int t) {
|
||||
super(id, n, t);
|
||||
this.shareT = null;
|
||||
this.ysDoneFlag = false;
|
||||
this.verifiableValues = new ArrayList<T>(this.commitments);
|
||||
this.recoverSharesSet = new HashSet<Polynomial.Point>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
package meerkat.crypto.dkg.gjkr;
|
||||
|
||||
import meerkat.crypto.dkg.comm.MessageUtils;
|
||||
import meerkat.crypto.secretsharing.feldman.VerifiableSecretSharing;
|
||||
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.protobuf.DKG;
|
||||
import org.factcenter.qilin.primitives.Group;
|
||||
import org.factcenter.qilin.util.ByteEncoder;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 3/16/2016.
|
||||
* TODO: comments
|
||||
* TODO: put Channel (ChannelImpl) in constructor
|
||||
*/
|
||||
public class Protocol<T> extends meerkat.crypto.dkg.feldman.Protocol<T> {
|
||||
|
||||
private VerifiableSecretSharing<T> maskingShares;
|
||||
private final T h;
|
||||
private Party<T>[] parties;
|
||||
|
||||
public Protocol(int t, int n, BigInteger zi, Random random, BigInteger q, T g
|
||||
, T h, Group<T> group, int id, ByteEncoder<T> byteEncoder) {
|
||||
super(t, n, zi, random, q, g, group, id,byteEncoder);
|
||||
this.h = h;
|
||||
BigInteger r = new BigInteger(q.bitLength(),random).mod(q);
|
||||
this.maskingShares = new VerifiableSecretSharing(t,n,r,random,q,h,group);
|
||||
this.parties = new Party[n];
|
||||
for (int i = 1; i <= n ; i++){
|
||||
this.parties[i - 1] = new Party(i,n,t);
|
||||
}
|
||||
this.parties[id - 1].share = getShare(id);
|
||||
this.parties[id - 1].shareT = maskingShares.getShare(id);
|
||||
super.setParties(parties);
|
||||
}
|
||||
|
||||
protected Party[] getParties(){
|
||||
return parties;
|
||||
}
|
||||
|
||||
protected void setParties(Party[] parties) {
|
||||
super.setParties(parties);
|
||||
this.parties = parties;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void sendSecret(int j) {
|
||||
Polynomial.Point secret = getShare(j);
|
||||
Polynomial.Point secretT = maskingShares.getShare(j);
|
||||
DKG.ShareMessage doubleSecretMessage = createShareMessage(id,j,secret,secretT);
|
||||
// TODO: Change SHARE to SHARE
|
||||
channel.sendMessage(j, MessageUtils.createMessage(DKG.Payload.Type.SHARE, doubleSecretMessage));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isValidShare(int i){
|
||||
Party party = parties[i - 1];
|
||||
return isValidShare(party.share, party.shareT, party.verifiableValues, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* test if share, shareT are valid with respect to verificationValues
|
||||
* @param share
|
||||
* @param shareT
|
||||
* @param verificationValues
|
||||
* @param j
|
||||
* @return computeVerificationValue(j,verificationValues,group) == (g ^ share.y) * (h ^ shareT.y) mod q
|
||||
*/
|
||||
public boolean isValidShare(Polynomial.Point share, Polynomial.Point shareT, ArrayList<T> verificationValues, int j){
|
||||
try {
|
||||
T v = computeVerificationValue(j, verificationValues, group);
|
||||
T exp = group.add(group.multiply(g, share.y), group.multiply(h, shareT.y));
|
||||
return exp.equals(v);
|
||||
}
|
||||
catch (NullPointerException e){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create complaint message against i and broadcast it
|
||||
* @param share
|
||||
* @param shareT
|
||||
* @param i
|
||||
*/
|
||||
private void broadcastComplaint(Polynomial.Point share, Polynomial.Point shareT, int i){
|
||||
DKG.ShareMessage complaint = createShareMessage(i,id,share,shareT);
|
||||
channel.broadcastMessage(MessageUtils.createMessage(DKG.Payload.Type.COMPLAINT, complaint));
|
||||
}
|
||||
|
||||
/**
|
||||
* stage4.3 according to the protocol
|
||||
* if check fails for index i, Pj
|
||||
*/
|
||||
public void computeAndBroadcastComplaints(Set<Integer> QUAL){
|
||||
Party party;
|
||||
for (int i : QUAL) {
|
||||
party = parties[i - 1];
|
||||
if (i != id) {
|
||||
if (!super.isValidShare(party.share, party.commitments, id)) {
|
||||
broadcastComplaint(party.share, party.shareT, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* compute verification values and broadcast them
|
||||
* verificationValues[k] = g ^ commitments [k] * h ^ maskingShares.commitments [k]
|
||||
*/
|
||||
public void computeAndBroadcastVerificationValues(){
|
||||
ArrayList<T> verificationValues = new ArrayList<T>(t+1);
|
||||
ArrayList<T> hBaseCommitments = maskingShares.getCommitmentsArrayList();
|
||||
for (int k = 0 ; k <= t ; k++){
|
||||
verificationValues.add(k,group.add(commitmentsArrayList.get(k),hBaseCommitments.get(k)));
|
||||
}
|
||||
broadcastCommitments(verificationValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* pack share, shareT i,j to createShareMessage
|
||||
* @param i
|
||||
* @param j
|
||||
* @param share
|
||||
* @param shareT
|
||||
* @return
|
||||
*/
|
||||
|
||||
private DKG.ShareMessage createShareMessage(int i, int j, Polynomial.Point share, Polynomial.Point shareT){
|
||||
DKG.ShareMessage ShareMessage = DKG.ShareMessage.newBuilder()
|
||||
.setI(i)
|
||||
.setJ(j)
|
||||
.setShare(ByteString.copyFrom(share.y.toByteArray()))
|
||||
.setShareT(ByteString.copyFrom(shareT.y.toByteArray()))
|
||||
.build();
|
||||
return ShareMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastComplaintAnswer(int j) {
|
||||
DKG.ShareMessage answer = createShareMessage(id,j,getShare(j)
|
||||
, maskingShares.getShare(j));
|
||||
channel.broadcastMessage(MessageUtils.createMessage(DKG.Payload.Type.ANSWER, answer));
|
||||
}
|
||||
|
||||
public void broadcastAnswer(Polynomial.Point secret, Polynomial.Point secretT, int i){
|
||||
DKG.ShareMessage complaint = createShareMessage(i,id,secret,secretT);
|
||||
channel.broadcastMessage(MessageUtils.createMessage(DKG.Payload.Type.ANSWER,complaint));
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return h
|
||||
*/
|
||||
public T getH() {
|
||||
return h;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,359 @@
|
|||
package meerkat.crypto.dkg.gjkr;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import meerkat.crypto.utils.Arithmetic;
|
||||
import meerkat.crypto.utils.concrete.Fp;
|
||||
import meerkat.comm.Channel;
|
||||
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||
import meerkat.crypto.secretsharing.shamir.SecretSharing;
|
||||
import meerkat.protobuf.Comm;
|
||||
import meerkat.protobuf.DKG;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static meerkat.crypto.dkg.comm.MessageUtils.*;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 3/16/2016.
|
||||
* <p/>
|
||||
* implementation of gjkr protocol user.
|
||||
* <p/>
|
||||
* this protocol extends joint Feldman protocol by splitting the protocol to commitment stage (stages 1,2,3)
|
||||
* and revealing stage (stage 4).
|
||||
* <p/>
|
||||
* as in joint Feldman, each party in QUAL has his own share of the generated random key.
|
||||
* this key can be recover by any subset of QUAL of size at least t + 1.
|
||||
*/
|
||||
public class User<T> extends meerkat.crypto.dkg.feldman.User<T> {
|
||||
|
||||
/**
|
||||
* All parties participating in key generation.
|
||||
* parties[id-1] has my info.
|
||||
*/
|
||||
protected Party<T>[] parties;
|
||||
|
||||
/**
|
||||
* gjkr secure protocol object
|
||||
*/
|
||||
protected final Protocol<T> sdkg;
|
||||
|
||||
boolean isStage4;
|
||||
|
||||
/**
|
||||
* constructor
|
||||
*
|
||||
* @param sdkg gjkr protocol object
|
||||
* @param channel channel object
|
||||
*/
|
||||
public User(Protocol<T> sdkg, Channel channel) {
|
||||
super(sdkg, channel);
|
||||
this.sdkg = sdkg;
|
||||
this.parties = sdkg.getParties();
|
||||
}
|
||||
|
||||
/**
|
||||
* stage1 according to the protocol
|
||||
* 1. Pi broadcasts Cik=Aik*Bik for k = 0,...,t.
|
||||
* 2. Pi computes the shares Sij,Sij' for j = 1,...,n and sends Sij,Sij' secretly to Pj.
|
||||
*/
|
||||
@Override
|
||||
protected void stage1() {
|
||||
sdkg.computeAndBroadcastVerificationValues();
|
||||
sdkg.sendSecrets();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void waitUntilStageOneCompleted() {
|
||||
super.waitUntilStageOneCompleted();
|
||||
// save the received commitments as verification values
|
||||
ArrayList<T> temp;
|
||||
for (int i = 0; i < n; i++) {
|
||||
temp = parties[i].verifiableValues;
|
||||
parties[i].verifiableValues = parties[i].commitments;
|
||||
parties[i].commitments = temp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* stage2 according to the protocol
|
||||
* Pj verifies all the shares,sharesT he received
|
||||
* if check fails for an index i, Pj broadcasts a complaint against Pi.
|
||||
* Pj broadcasts done message at the end of this stage
|
||||
*/
|
||||
@Override
|
||||
protected void stage2() {
|
||||
sdkg.broadcastComplaints();
|
||||
//broadcast done message after all complaints
|
||||
channel.broadcastMessage(createMessage(DKG.Payload.Type.DONE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all non-aborting qualified parties have sent commitments.
|
||||
* @return
|
||||
*/
|
||||
protected boolean haveAllQualPartiesCommitted() {
|
||||
for (int i : QUAL) {
|
||||
if (parties[i - 1].aborted)
|
||||
continue;
|
||||
for (int k = 0; k <= t; k++) {
|
||||
if (parties[i - 1].commitments.get(k) == null)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if all non-aborting qualified parties sent a done message
|
||||
* @return
|
||||
*/
|
||||
protected boolean areAllQualPartiesDone() {
|
||||
for (int i : QUAL) {
|
||||
if (parties[i - 1].aborted)
|
||||
continue;
|
||||
for (int k = 0; k <= t; k++) {
|
||||
if (!parties[i - 1].ysDoneFlag)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if at least t + 1 secrets were received foreach i in QUAL that aborted
|
||||
* @return
|
||||
*/
|
||||
protected boolean haveReceivedEnoughSecretShares() {
|
||||
for (int i : QUAL) {
|
||||
if (parties[i - 1].aborted && parties[i - 1].recoverSharesSet.size() <= t)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* broadcast commitments and recover parties information if necessary
|
||||
*/
|
||||
private void resolveQualifyingPublicKey() {
|
||||
sdkg.broadcastCommitments();
|
||||
// wait until all parties in QUAL broadcast their commitments or aborted
|
||||
while (!stop && !haveAllQualPartiesCommitted())
|
||||
waitAndHandleReceivedMessages();
|
||||
|
||||
if (stop)
|
||||
return;
|
||||
|
||||
sdkg.computeAndBroadcastComplaints(QUAL);
|
||||
|
||||
//broadcast done message after all complaints
|
||||
channel.broadcastMessage(createMessage(DKG.Payload.Type.DONE));
|
||||
|
||||
// wait until all parties in QUAL done or aborted
|
||||
while (!stop && !areAllQualPartiesDone())
|
||||
waitAndHandleReceivedMessages();
|
||||
|
||||
if (stop)
|
||||
return;
|
||||
|
||||
// broadcast i private secret foreach i in QUAL that aborted
|
||||
for (int i : QUAL) {
|
||||
if (parties[i - 1].aborted) {
|
||||
sdkg.broadcastAnswer(parties[i - 1].share, parties[i - 1].shareT, i);
|
||||
}
|
||||
}
|
||||
// wait until at least t + 1 secrets will received foreach i in QUAL that aborted
|
||||
while (!stop && !haveReceivedEnoughSecretShares())
|
||||
waitAndHandleReceivedMessages();
|
||||
|
||||
if (stop)
|
||||
return;
|
||||
|
||||
Arithmetic<BigInteger> arithmetic = new Fp(sdkg.getQ());
|
||||
// restore necessary information
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (parties[i].recoverSharesSet.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
Polynomial.Point[] shares = new Polynomial.Point[t + 1];
|
||||
int j = 0;
|
||||
for (Polynomial.Point share : parties[i].recoverSharesSet) {
|
||||
shares[j++] = share;
|
||||
if (j >= shares.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Polynomial polynomial = SecretSharing.recoverPolynomial(shares, arithmetic);
|
||||
BigInteger[] coefficients = polynomial.getCoefficients();
|
||||
for (int k = 0; k <= t; k++) {
|
||||
parties[i].commitments.add(k, group.multiply(g, coefficients[k]));
|
||||
}
|
||||
parties[i].share = new Polynomial.Point(BigInteger.valueOf(id), polynomial);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* notifies message handler and message handler that stage 4 was started
|
||||
*/
|
||||
protected void setStage4() {
|
||||
isStage4 = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stage4() {
|
||||
setStage4();
|
||||
resolveQualifyingPublicKey();
|
||||
if (stop) return;
|
||||
super.stage4();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* if !isStage4 as super, with extension to double secret message
|
||||
* else answer message is valid if:
|
||||
* 1. it was received in broadcast chanel
|
||||
* 2. secret.j == sender
|
||||
* 3. QUAL contains i and j
|
||||
*/
|
||||
protected boolean isValidAnswerMessage(int sender, boolean isBroadcast, DKG.ShareMessage doubleSecretMessage) {
|
||||
if (!isStage4) {
|
||||
return super.isValidAnswerMessage(sender, isBroadcast, doubleSecretMessage);
|
||||
} else {
|
||||
int i = doubleSecretMessage.getI();
|
||||
int j = doubleSecretMessage.getJ();
|
||||
return isBroadcast && j == sender && parties[i - 1].aborted && !parties[j - 1].aborted
|
||||
&& QUAL.contains(i) && QUAL.contains(j);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* as in super with respect to protocol stage
|
||||
*/
|
||||
@Override
|
||||
protected boolean isValidDoneMessage(int sender, boolean isBroadcast) {
|
||||
if (!isStage4) {
|
||||
return super.isValidDoneMessage(sender, isBroadcast);
|
||||
} else {
|
||||
return isBroadcast && !parties[sender - 1].ysDoneFlag;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* use only in stage4
|
||||
* complaint message is valid if:
|
||||
* 1. it was received in broadcast chanel
|
||||
* 2. secret.j == sender
|
||||
* 3. QUAL contains i and j
|
||||
*/
|
||||
protected boolean isValidComplaintMessage(int sender, boolean isBroadcast,
|
||||
DKG.ShareMessage complaintMessage) {
|
||||
int i = complaintMessage.getI();
|
||||
int j = complaintMessage.getJ();
|
||||
return isBroadcast && j == sender && QUAL.contains(i) && QUAL.contains(j);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleMessage(Comm.BroadcastMessage envelope) throws InvalidProtocolBufferException {
|
||||
int sender = envelope.getSender();
|
||||
boolean isBroadcast = !envelope.getIsPrivate();
|
||||
DKG.Payload msg = DKG.Payload.parseFrom(envelope.getPayload());
|
||||
switch (msg.getType()) {
|
||||
case SHARE:
|
||||
/**
|
||||
* as in super, with extension to double secret message
|
||||
*/
|
||||
DKG.ShareMessage doubleSecretMessage = msg.getShare();
|
||||
if (isValidSecretMessage(sender, isBroadcast, doubleSecretMessage)) {
|
||||
int i = doubleSecretMessage.getI();
|
||||
synchronized (parties[i - 1]) {
|
||||
parties[i - 1].share = extractShare(id, doubleSecretMessage.getShare());
|
||||
parties[i - 1].shareT = extractShare(id, doubleSecretMessage.getShareT());
|
||||
parties[i - 1].notify();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ANSWER:
|
||||
/**
|
||||
* if !isStage4 as super, with extension to double secret message
|
||||
* else saves secret
|
||||
*/
|
||||
assert msg.getPayloadDataCase() == DKG.Payload.PayloadDataCase.SHARE;
|
||||
doubleSecretMessage = msg.getShare();
|
||||
if (isValidAnswerMessage(sender, isBroadcast, doubleSecretMessage)) {
|
||||
int i = doubleSecretMessage.getI();
|
||||
int j = doubleSecretMessage.getJ();
|
||||
Polynomial.Point secret = extractShare(j, doubleSecretMessage.getShare());
|
||||
Polynomial.Point secretT = extractShare(j, doubleSecretMessage.getShareT());
|
||||
synchronized (parties[i - 1]) {
|
||||
if (!isStage4) {
|
||||
if (sdkg.isValidShare(secret, secretT, parties[j - 1].verifiableValues, i)) {
|
||||
parties[i - 1].complaints[j - 1] = meerkat.crypto.dkg.feldman.Protocol.ComplaintState.NonDisqualified;
|
||||
|
||||
} else {
|
||||
parties[i - 1].complaints[j - 1] = meerkat.crypto.dkg.feldman.Protocol.ComplaintState.Disqualified;
|
||||
}
|
||||
if (j == id) {
|
||||
parties[i - 1].share = secret;
|
||||
parties[i - 1].shareT = secretT;
|
||||
}
|
||||
} else if (sdkg.isValidShare(secret, secretT, parties[i - 1].verifiableValues, j)) {
|
||||
parties[i - 1].recoverSharesSet.add(secret);
|
||||
}
|
||||
parties[i - 1].notify();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DONE:
|
||||
/**
|
||||
* as in super with respect to protocol state
|
||||
*/
|
||||
if (!isStage4)
|
||||
super.handleMessage(envelope);
|
||||
else {
|
||||
if (isValidDoneMessage(sender, isBroadcast)) {
|
||||
synchronized (parties[sender - 1]) {
|
||||
parties[sender - 1].ysDoneFlag = true;
|
||||
parties[sender - 1].notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case COMPLAINT:
|
||||
/**
|
||||
* if !isStage4 as in super
|
||||
* else if secret,secretT are valid with respect to verifiableValues but
|
||||
* secret is not valid with respect to commitments then
|
||||
* marks i as aborted
|
||||
*/
|
||||
if (!isStage4) {
|
||||
super.handleMessage(envelope);
|
||||
} else {
|
||||
assert (msg.getPayloadDataCase() == DKG.Payload.PayloadDataCase.SHARE);
|
||||
DKG.ShareMessage ysComplaintMessage = msg.getShare();
|
||||
if (isValidComplaintMessage(sender, isBroadcast, ysComplaintMessage)) {
|
||||
int i = ysComplaintMessage.getI();
|
||||
int j = ysComplaintMessage.getJ();
|
||||
Polynomial.Point secret = extractShare(i, ysComplaintMessage.getShare());
|
||||
Polynomial.Point secretT = extractShare(i, ysComplaintMessage.getShareT());
|
||||
if (sdkg.isValidShare(secret, secretT, parties[i - 1].verifiableValues, j)
|
||||
&& !dkg.isValidShare(secret, parties[i - 1].commitments, j)) {
|
||||
synchronized (parties[i - 1]) {
|
||||
parties[i - 1].aborted = true;
|
||||
parties[i - 1].notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
super.handleMessage(envelope);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package meerkat.crypto.secretsharing.feldman;
|
||||
|
||||
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||
import meerkat.crypto.secretsharing.shamir.SecretSharing;
|
||||
import org.factcenter.qilin.primitives.Group;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 1/27/2016.
|
||||
*
|
||||
* an implementation of Feldman's verifiable secret sharing scheme.
|
||||
*
|
||||
* allows trusted dealer to share a key x among n parties.
|
||||
*
|
||||
*/
|
||||
public class VerifiableSecretSharing<T> extends SecretSharing {
|
||||
/**
|
||||
* cyclic group contains g.
|
||||
*/
|
||||
protected final Group<T> group;
|
||||
/**
|
||||
* a generator of cyclic group of order q.
|
||||
* the generated group is a subgroup of the given group.
|
||||
* it must be chosen such that computing discrete logarithms is hard in this group.
|
||||
*/
|
||||
protected final T g;
|
||||
/**
|
||||
* commitments to polynomial coefficients.
|
||||
* commitments[k] = g ^ coefficients[k] (group operation)
|
||||
*/
|
||||
protected final ArrayList<T> commitmentsArrayList;
|
||||
|
||||
/**
|
||||
* constructor
|
||||
* @param q a large prime.
|
||||
* @param t threshold. Any t+1 share holders can recover the secret,
|
||||
* but any set of at most t share holders cannot
|
||||
* @param n number of share holders
|
||||
* @param zi secret, chosen from Zq
|
||||
* @param random use for generate random polynomial
|
||||
* @param group
|
||||
* @param q a large prime dividing group order.
|
||||
* @param g a generator of cyclic group of order q.
|
||||
* the generated group is a subgroup of the given group.
|
||||
* it must be chosen such that computing discrete logarithms is hard in this group.
|
||||
*/
|
||||
public VerifiableSecretSharing(int t, int n, BigInteger zi, Random random, BigInteger q, T g
|
||||
, Group<T> group) {
|
||||
super(t, n, zi, random,q);
|
||||
this.g = g;
|
||||
this.group = group;
|
||||
assert (this.group.contains(g));
|
||||
this.commitmentsArrayList = generateCommitments();
|
||||
}
|
||||
|
||||
/**
|
||||
* commitments[i] = g ^ polynomial.coefficients[i]
|
||||
* @return commitments
|
||||
*/
|
||||
private ArrayList<T> generateCommitments() {
|
||||
|
||||
Polynomial polynomial = getPolynomial();
|
||||
BigInteger[] coefficients = polynomial.getCoefficients();
|
||||
ArrayList<T> commitments = new ArrayList<T>(t + 1);
|
||||
for (int i = 0 ; i <= t;i++){
|
||||
commitments.add(i,group.multiply(g,coefficients[i]));
|
||||
}
|
||||
return commitments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute verification value (g^{share value}) using coefficient commitments sent by dealer and my share id.
|
||||
* @param j my share holder id
|
||||
* @param commitments commitments to polynomial coefficients of share (received from dealer)
|
||||
* @param group
|
||||
*
|
||||
* @return product of Aik ^ (j ^ k) == g ^ polynomial(i)
|
||||
*/
|
||||
public static <T> T computeVerificationValue(int j, ArrayList<T> commitments, Group<T> group) {
|
||||
T v = group.zero();
|
||||
BigInteger power = BigInteger.ONE;
|
||||
BigInteger J = BigInteger.valueOf(j);
|
||||
for (int k = 0 ; k < commitments.size() ; k ++){
|
||||
v = group.add(v,group.multiply(commitments.get(k),power));
|
||||
power = power.multiply(J);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return generator of group
|
||||
*/
|
||||
public T getGenerator() {
|
||||
return g;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return group
|
||||
*/
|
||||
public Group<T> getGroup(){
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return commitmentsArrayList
|
||||
*/
|
||||
public ArrayList<T> getCommitmentsArrayList() {
|
||||
return commitmentsArrayList;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package meerkat.crypto.secretsharing.shamir;
|
||||
|
||||
import meerkat.crypto.utils.Arithmetic;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 1/28/2016.
|
||||
*
|
||||
* container of lagrange polynomial
|
||||
*
|
||||
* Constructor is private (use {@link #lagrangePolynomials(Polynomial.Point[], Arithmetic)} to construct)
|
||||
*
|
||||
* l = (evaluate/divisor)* polynomial
|
||||
*
|
||||
* Note : image and divisor stored separately for avoiding lose of information by division
|
||||
*/
|
||||
class LagrangePolynomial{
|
||||
public final Polynomial polynomial;
|
||||
public final BigInteger image;
|
||||
public final BigInteger divisor;
|
||||
|
||||
/**
|
||||
* inner constructor, stores all given parameters
|
||||
* @param polynomial
|
||||
* @param image
|
||||
* @param divisor
|
||||
*/
|
||||
private LagrangePolynomial(Polynomial polynomial, BigInteger image, BigInteger divisor) {
|
||||
this.polynomial = polynomial;
|
||||
this.image = image;
|
||||
this.divisor = divisor;
|
||||
}
|
||||
|
||||
/**
|
||||
* static method
|
||||
* @param points array points s.t there are no couple of points that shares the same x value
|
||||
*
|
||||
* @return the lagrange polynomials that mach to given points.
|
||||
* in case there exists i != j s.t points[i].x == points[j].x returns null.
|
||||
*/
|
||||
public static LagrangePolynomial[] lagrangePolynomials(Polynomial.Point[] points,Arithmetic<BigInteger> arithmetic) {
|
||||
Polynomial one = new Polynomial(new BigInteger[]{BigInteger.ONE},arithmetic);
|
||||
LagrangePolynomial[] lagrangePolynomials = new LagrangePolynomial[points.length];
|
||||
Polynomial[] factors = new Polynomial[points.length];
|
||||
for (int i = 0 ; i < factors.length ; i++){
|
||||
factors[i] = new Polynomial(new BigInteger[]{points[i].x.negate(),BigInteger.ONE},arithmetic); // X - Xi
|
||||
}
|
||||
Polynomial product;
|
||||
BigInteger divisor;
|
||||
for(int i = 0; i < points.length; i ++) {
|
||||
product = one;
|
||||
divisor = BigInteger.ONE;
|
||||
for (int j = 0; j < points.length; j++) {
|
||||
if (i != j) {
|
||||
divisor = arithmetic.mul(divisor,arithmetic.sub(points[i].x,points[j].x));
|
||||
product = product.mul(factors[j]);
|
||||
}
|
||||
}
|
||||
if(divisor.equals(BigInteger.ZERO))
|
||||
return null;
|
||||
lagrangePolynomials[i] = new LagrangePolynomial(product,points[i].y,divisor);
|
||||
}
|
||||
return lagrangePolynomials;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
package meerkat.crypto.secretsharing.shamir;
|
||||
|
||||
import meerkat.crypto.utils.Arithmetic;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 1/27/2016.
|
||||
*/
|
||||
public class Polynomial implements Comparable<Polynomial> {
|
||||
private final int degree;
|
||||
private final BigInteger[] coefficients;
|
||||
private final Arithmetic<BigInteger> arithmetic;
|
||||
|
||||
/**
|
||||
* constructor
|
||||
* @param coefficients
|
||||
* @param arithmetic
|
||||
* degree set as max index such that coefficients[degree] not equals zero
|
||||
*/
|
||||
public Polynomial(BigInteger[] coefficients,Arithmetic<BigInteger> arithmetic) {
|
||||
int d = coefficients.length - 1;
|
||||
while (d > 0 && coefficients[d].equals(BigInteger.ZERO)){
|
||||
d--;
|
||||
}
|
||||
this.degree = d;
|
||||
this.coefficients = coefficients;
|
||||
this.arithmetic = arithmetic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare to another polynomial (order by degree, then coefficients).
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(Polynomial other) {
|
||||
if (this.degree != other.degree)
|
||||
return this.degree - other.degree;
|
||||
int compare;
|
||||
for (int i = degree; i >= degree ; i--){
|
||||
compare = this.coefficients[i].compareTo(other.coefficients[i]);
|
||||
if (compare != 0){
|
||||
return compare;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x
|
||||
* @return sum of coefficients[i] * (x ^ i)
|
||||
*/
|
||||
public BigInteger evaluate(BigInteger x){
|
||||
BigInteger result = BigInteger.ZERO;
|
||||
BigInteger power = BigInteger.ONE;
|
||||
for(int i = 0 ; i <= degree ; i++){
|
||||
result = arithmetic.add(result,arithmetic.mul(coefficients[i],power));
|
||||
power = power.multiply(x);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param points
|
||||
* @return polynomial of minimal degree which goes through all points.
|
||||
* If there exists i != j s.t points[i].x == points[j].x, method returns null.
|
||||
*/
|
||||
public static Polynomial interpolation(Point[] points, Arithmetic<BigInteger> arithmetic) {
|
||||
LagrangePolynomial[] l = LagrangePolynomial.lagrangePolynomials(points,arithmetic);
|
||||
if (l == null){
|
||||
return null;
|
||||
}
|
||||
// product = product of l[i].divisor
|
||||
BigInteger product = BigInteger.ONE;
|
||||
for (int i = 0; i < l.length;i++){
|
||||
product = arithmetic.mul(product,l[i].divisor);
|
||||
}
|
||||
|
||||
// factor[i] = product divided by l[i].divisor = product of l[j].divisor s.t j!=i
|
||||
BigInteger[] factors = new BigInteger[l.length];
|
||||
for (int i = 0; i < l.length;i++){
|
||||
factors[i] = arithmetic.div(product,l[i].divisor);
|
||||
}
|
||||
int degree = l[0].polynomial.degree;
|
||||
|
||||
// coefficients[j] = (sum of l[i].evaluate * factor[i] * l[i].coefficients[j] s.t i!=j) divide by product =
|
||||
// = sum of l[i].evaluate * l[i].coefficients[j] / l[i].divisor s.t i!=j
|
||||
BigInteger[] coefficients = new BigInteger[degree + 1];
|
||||
for (int j = 0; j < coefficients.length;j++){
|
||||
coefficients[j] = BigInteger.ZERO;
|
||||
for (int i = 0; i < l.length; i++){
|
||||
BigInteger current = arithmetic.mul(l[i].image,factors[i]);
|
||||
current = arithmetic.mul(current,l[i].polynomial.coefficients[j]);
|
||||
coefficients[j] = arithmetic.add(coefficients[j],current);
|
||||
}
|
||||
coefficients[j] = arithmetic.div(coefficients[j],product);
|
||||
}
|
||||
return new Polynomial(coefficients,arithmetic);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param other
|
||||
* @return new Polynomial of degree max(this degree,other degree) s.t for all x
|
||||
* new.evaluate(x) = this.evaluate(x) + other.evaluate(x)
|
||||
*/
|
||||
public Polynomial add(Polynomial other){
|
||||
Polynomial bigger,smaller;
|
||||
if(this.degree < other.degree){
|
||||
bigger = other;
|
||||
smaller = this;
|
||||
}else{
|
||||
bigger = this;
|
||||
smaller = other;
|
||||
}
|
||||
BigInteger[] coefficients = bigger.getCoefficients();
|
||||
|
||||
for (int i = 0; i <= smaller.degree ; i++){
|
||||
coefficients[i] = arithmetic.add(smaller.coefficients[i],bigger.coefficients[i]);
|
||||
}
|
||||
return new Polynomial(coefficients,other.arithmetic);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param constant
|
||||
* @return new Polynomial of degree this.degree s.t for all x
|
||||
* new.evaluate(x) = constant * this.evaluate(x)
|
||||
*/
|
||||
public Polynomial mul(BigInteger constant){
|
||||
|
||||
BigInteger[] coefficients = this.getCoefficients();
|
||||
|
||||
for (int i = 0; i <= this.degree ; i++){
|
||||
coefficients[i] = arithmetic.mul(constant,coefficients[i]);
|
||||
}
|
||||
return new Polynomial(coefficients,arithmetic);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param other
|
||||
* @return new Polynomial of degree this degree + other degree + 1 s.t for all x
|
||||
* new.evaluate(x) = this.evaluate(x) * other.evaluate(x)
|
||||
*/
|
||||
public Polynomial mul(Polynomial other){
|
||||
|
||||
BigInteger[] coefficients = new BigInteger[this.degree + other.degree + 1];
|
||||
Arrays.fill(coefficients,BigInteger.ZERO);
|
||||
|
||||
for (int i = 0; i <= this.degree ; i++){
|
||||
for (int j = 0; j <= other.degree; j++){
|
||||
coefficients[i+j] = arithmetic.add(coefficients[i+j],arithmetic.mul(this.coefficients[i],other.coefficients[j]));
|
||||
}
|
||||
}
|
||||
return new Polynomial(coefficients,arithmetic);
|
||||
}
|
||||
|
||||
|
||||
/** getter
|
||||
* @return copy of coefficients
|
||||
*/
|
||||
public BigInteger[] getCoefficients() {
|
||||
return Arrays.copyOf(coefficients,coefficients.length);
|
||||
}
|
||||
|
||||
/** getter
|
||||
* @return degree
|
||||
*/
|
||||
public int getDegree() {
|
||||
return degree;
|
||||
}
|
||||
|
||||
/**
|
||||
* inner class
|
||||
* container for (x,y) x from range and y from evaluate of polynomial
|
||||
*/
|
||||
public static class Point implements java.io.Serializable {
|
||||
public final BigInteger x;
|
||||
public final BigInteger y;
|
||||
|
||||
/**
|
||||
* constructor
|
||||
* @param x
|
||||
* @param polynomial y = polynomial.evaluate(x)
|
||||
*/
|
||||
public Point(BigInteger x, Polynomial polynomial) {
|
||||
this.x = x;
|
||||
this.y = polynomial.evaluate(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* constructor
|
||||
* @param x
|
||||
* @param y
|
||||
*/
|
||||
public Point(BigInteger x,BigInteger y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(!super.equals(obj))
|
||||
return false;
|
||||
Point other = (Point)obj;
|
||||
return this.x.equals(other.x) && this.y.equals(other.y);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
package meerkat.crypto.secretsharing.shamir;
|
||||
|
||||
import meerkat.crypto.utils.Arithmetic;
|
||||
import meerkat.crypto.utils.concrete.Fp;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 1/27/2016.
|
||||
* an implementation of Shamire's secret sharing scheme
|
||||
*/
|
||||
public class SecretSharing{
|
||||
/**
|
||||
* threshold
|
||||
*/
|
||||
protected final int t;
|
||||
/**
|
||||
* number of shares
|
||||
*/
|
||||
protected final int n;
|
||||
/**
|
||||
* a large prime
|
||||
*/
|
||||
protected final BigInteger q;
|
||||
/**
|
||||
* random polynomial of degree s.t polynomial.evaluate(0) = secret
|
||||
*/
|
||||
protected final Polynomial polynomial;
|
||||
|
||||
/**
|
||||
* constructor
|
||||
* @param q a large prime.
|
||||
* @param t threshold. Any t+1 share holders can recover the secret,
|
||||
* but any set of at most t share holders cannot
|
||||
* @param n number of share holders
|
||||
* @param zi secret, chosen from Zq
|
||||
* @param random use for generate random polynomial
|
||||
*/
|
||||
public SecretSharing(int t, int n, BigInteger zi, Random random, BigInteger q) {
|
||||
this.q = q;
|
||||
this.t = t;
|
||||
this.n = n;
|
||||
this.polynomial = generateRandomPolynomial(zi,random);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x
|
||||
* @param random
|
||||
* @return new Polynomial polynomial of degree t ,such that
|
||||
* 1. polynomial(0) = x
|
||||
* 2. polynomial coefficients randomly chosen from Zq (except of coefficients[0] = x)
|
||||
*/
|
||||
private Polynomial generateRandomPolynomial(BigInteger x, Random random) {
|
||||
BigInteger[] coefficients = new BigInteger[t + 1];
|
||||
coefficients[0] = x.mod(q);
|
||||
int bits = q.bitLength();
|
||||
for (int i = 1 ; i <= t; i++ ){
|
||||
coefficients[i] = new BigInteger(bits,random).mod(q);
|
||||
}
|
||||
return new Polynomial(coefficients,new Fp(q));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param i in range of [1,...n]
|
||||
*
|
||||
* @return polynomial.evaluate(i)
|
||||
*/
|
||||
public Polynomial.Point getShare(int i){
|
||||
assert (i > 0 && i <= n);
|
||||
return new Polynomial.Point(BigInteger.valueOf(i), polynomial);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param shares - subset of the original shares
|
||||
*
|
||||
* @return evaluate of interpolation(shares) at x = 0
|
||||
*/
|
||||
public static BigInteger recoverSecret(Polynomial.Point[] shares, Arithmetic<BigInteger> arithmetic) {
|
||||
return recoverPolynomial(shares,arithmetic).evaluate(BigInteger.ZERO);
|
||||
}
|
||||
/**
|
||||
* @param shares - subset of the original shares
|
||||
*
|
||||
* @return interpolation(shares)
|
||||
*/
|
||||
public static Polynomial recoverPolynomial(Polynomial.Point[] shares, Arithmetic<BigInteger> arithmetic) {
|
||||
return Polynomial.interpolation(shares,arithmetic);
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return threshold
|
||||
*/
|
||||
public int getT() {
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return number of share holders
|
||||
*/
|
||||
public int getN() {
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return the prime was given in the constructor
|
||||
*/
|
||||
public BigInteger getQ() {
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getter
|
||||
* @return the polynomial was generated in constructor
|
||||
*/
|
||||
public Polynomial getPolynomial() {
|
||||
return polynomial;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package meerkat.crypto.utils;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 3/17/2016.
|
||||
* defines the properties of the traditional operations : add,sub,mul,div
|
||||
* between two objects of type T
|
||||
*/
|
||||
public interface Arithmetic<T> {
|
||||
/**
|
||||
* addition
|
||||
* @param a
|
||||
* @param b
|
||||
* @return a + b
|
||||
*/
|
||||
T add(T a, T b);
|
||||
/**
|
||||
* subtraction
|
||||
* @param a
|
||||
* @param b
|
||||
* @return a - b
|
||||
*/
|
||||
T sub(T a, T b);
|
||||
/**
|
||||
* multiplication
|
||||
* @param a
|
||||
* @param b
|
||||
* @return a * b
|
||||
*/
|
||||
T mul(T a, T b);
|
||||
/**
|
||||
* division
|
||||
* @param a
|
||||
* @param b
|
||||
* @return a / b
|
||||
*/
|
||||
T div(T a, T b);
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package meerkat.crypto.utils.concrete;
|
||||
|
||||
import meerkat.crypto.utils.Arithmetic;
|
||||
import org.factcenter.qilin.primitives.concrete.Zpstar;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 3/17/2016.
|
||||
* an implementation of Arithmetic<BigInteger> over prime fields: integers modulo p
|
||||
*/
|
||||
public class Fp implements Arithmetic<BigInteger> {
|
||||
public final BigInteger p;
|
||||
private final Zpstar zp;
|
||||
|
||||
/**
|
||||
* constructor
|
||||
* @param p prime
|
||||
*/
|
||||
public Fp(BigInteger p) {
|
||||
this.p = p;
|
||||
this.zp = new Zpstar(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger add(BigInteger a, BigInteger b){
|
||||
return a.add(b).mod(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger sub(BigInteger a, BigInteger b){
|
||||
return a.add(p).subtract(b).mod(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger mul(BigInteger a, BigInteger b){
|
||||
return zp.add(a,b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger div(BigInteger a, BigInteger b){
|
||||
return mul(a,zp.negate(b));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package meerkat;
|
||||
|
||||
option java_package = "meerkat.protobuf";
|
||||
|
||||
message Payload {
|
||||
enum Type {
|
||||
SHARE = 0;
|
||||
COMMITMENT = 1;
|
||||
COMPLAINT = 2;
|
||||
DONE = 3;
|
||||
ANSWER = 4;
|
||||
YCOMMITMENT = 5;
|
||||
YCOMPLAINT = 6;
|
||||
YANSWER = 7;
|
||||
ABORT = 8;
|
||||
}
|
||||
|
||||
|
||||
// Type of message in protocol
|
||||
Type type = 1;
|
||||
|
||||
oneof payload_data {
|
||||
IDMessage id = 5;
|
||||
ShareMessage share = 6;
|
||||
CommitmentMessage commitment = 7;
|
||||
}
|
||||
}
|
||||
|
||||
message IDMessage {
|
||||
int32 id = 1;
|
||||
}
|
||||
|
||||
message ShareMessage {
|
||||
int32 i = 1;
|
||||
int32 j = 2;
|
||||
bytes share = 3;
|
||||
// For double shares (used in GJKR protocol)
|
||||
bytes share_t = 4;
|
||||
}
|
||||
|
||||
message CommitmentMessage {
|
||||
int32 k = 1;
|
||||
bytes commitment = 2;
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package meerkat.crypto.dkg.feldman;
|
||||
|
||||
import meerkat.comm.Channel;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 3/21/2016.
|
||||
*/
|
||||
public class DKGMaliciousUser<T> extends User<T> {
|
||||
|
||||
private final Protocol<T> maliciousDkg;
|
||||
private final Set<Integer> falls;
|
||||
public DKGMaliciousUser(Protocol<T> dkg, Protocol<T> maliciousDKG, Channel channel, Set<Integer> falls) {
|
||||
super(dkg, channel);
|
||||
this.falls = falls;
|
||||
this.maliciousDkg = maliciousDKG;
|
||||
maliciousDKG.setParties(parties);
|
||||
}
|
||||
|
||||
public static Set<Integer> selectFallsRandomly(Set<Integer> ids, Random random){
|
||||
Set<Integer> falls = new HashSet<Integer>();
|
||||
ArrayList<Integer> idsList = new ArrayList<Integer>();
|
||||
for (int id : ids){
|
||||
idsList.add(id);
|
||||
}
|
||||
int fallsSize = random.nextInt(idsList.size()) + 1;// 1 - (n-1)
|
||||
while (falls.size() < fallsSize){
|
||||
falls.add(idsList.remove(random.nextInt(idsList.size())));
|
||||
}
|
||||
return falls;
|
||||
}
|
||||
|
||||
public static <T> Protocol<T> generateMaliciousDKG(Protocol<T> dkg,Channel channel,Random random){
|
||||
BigInteger q = dkg.getQ();
|
||||
BigInteger zi = new BigInteger(q.bitLength(), random).mod(q);
|
||||
Protocol<T> malicious = new Protocol<T>(dkg.getT(),dkg.getN(),zi,random,dkg.getQ()
|
||||
,dkg.getGenerator(),dkg.getGroup(),dkg.getId(),dkg.getEncoder());
|
||||
malicious.setChannel(channel);
|
||||
return malicious;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stage1() {
|
||||
dkg.broadcastCommitments();
|
||||
sendSecrets(); //insteadof crypto.sendSecrets(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stage3() {
|
||||
maliciousDkg.answerAllComplainingPlayers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stage4(){
|
||||
// do nothing
|
||||
}
|
||||
|
||||
private void sendSecrets(){
|
||||
for (int j = 1; j <= n ; j++){
|
||||
if(j != id){
|
||||
if(falls.contains(j)){
|
||||
maliciousDkg.sendSecret(j);
|
||||
}else {
|
||||
dkg.sendSecret(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
package meerkat.crypto.dkg.feldman;
|
||||
|
||||
import meerkat.comm.ChannelImpl;
|
||||
import meerkat.crypto.utils.Arithmetic;
|
||||
import meerkat.crypto.utils.concrete.Fp;
|
||||
import meerkat.comm.Channel;
|
||||
import meerkat.crypto.secretsharing.feldman.VerifiableSecretSharing;
|
||||
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||
import meerkat.crypto.secretsharing.shamir.SecretSharing;
|
||||
import meerkat.crypto.utils.BigIntegerByteEncoder;
|
||||
import meerkat.crypto.utils.GenerateRandomPrime;
|
||||
import org.factcenter.qilin.primitives.Group;
|
||||
import org.factcenter.qilin.primitives.concrete.Zpstar;
|
||||
import org.factcenter.qilin.util.ByteEncoder;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 3/21/2016.
|
||||
*/
|
||||
public class DKGTest {
|
||||
|
||||
int tests = 10;
|
||||
BigInteger p = GenerateRandomPrime.SafePrime100Bits;
|
||||
BigInteger q = p.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2));
|
||||
Group<BigInteger> group = new Zpstar(p);
|
||||
Arithmetic<BigInteger> arithmetic = new Fp(q);
|
||||
int t = 9;
|
||||
int n = 20;
|
||||
|
||||
public void oneTest(Testable testable) throws Exception {
|
||||
for (int i = 0; i < testable.threads.length ; i++){
|
||||
testable.threads[i].start();
|
||||
}
|
||||
for (int i = 0; i < testable.threads.length ; i++){
|
||||
testable.threads[i].join();
|
||||
}
|
||||
|
||||
// got the right public value
|
||||
BigInteger publicValue = group.multiply(testable.g,testable.secret);
|
||||
for (int i: testable.valids){
|
||||
assert (testable.dkgs[i - 1].getPublicValue().equals(publicValue));
|
||||
}
|
||||
|
||||
// assert valid verification values
|
||||
BigInteger expected,verification;
|
||||
for (int i: testable.valids){
|
||||
expected = group.multiply(testable.g, testable.dkgs[i - 1].getShare().y);
|
||||
verification = VerifiableSecretSharing.computeVerificationValue(i, testable.dkgs[i - 1].getCommitments(), group);
|
||||
assert (expected.equals(verification));
|
||||
}
|
||||
|
||||
|
||||
// restore the secret from shares
|
||||
ArrayList<Polynomial.Point> sharesList = new ArrayList<Polynomial.Point>();
|
||||
|
||||
for (int i: testable.valids){
|
||||
sharesList.add(testable.dkgs[i - 1].getShare());
|
||||
}
|
||||
Polynomial.Point[] shares = new Polynomial.Point[sharesList.size()];
|
||||
for (int i = 0; i < shares.length; i ++){
|
||||
shares[i] = sharesList.get(i);
|
||||
}
|
||||
|
||||
BigInteger calculatedSecret = SecretSharing.recoverSecret(shares,arithmetic);
|
||||
assert (calculatedSecret.equals(testable.secret));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
Testable testable;
|
||||
for (int i = 0; i < tests; i++){
|
||||
testable = new Testable(new Random());
|
||||
oneTest(testable);
|
||||
}
|
||||
}
|
||||
|
||||
class Testable{
|
||||
Set<Integer> valids;
|
||||
Set<Integer> QUAL;
|
||||
Set<Integer> aborted;
|
||||
Set<Integer> malicious;
|
||||
User<BigInteger>[] dkgs;
|
||||
Thread[] threads;
|
||||
BigInteger g;
|
||||
BigInteger secret;
|
||||
|
||||
public Testable(Random random) {
|
||||
this.dkgs = new User[n];
|
||||
this.valids = new HashSet<Integer>();
|
||||
this.QUAL = new HashSet<Integer>();
|
||||
this.aborted = new HashSet<Integer>();
|
||||
this.malicious = new HashSet<Integer>();
|
||||
this.threads = new Thread[n];
|
||||
this.g = sampleGenerator(random);
|
||||
ArrayList<Integer> ids = new ArrayList<Integer>();
|
||||
for (int id = 1; id<= n ; id++){
|
||||
ids.add(id);
|
||||
}
|
||||
int id;
|
||||
BigInteger s;
|
||||
Protocol<BigInteger> dkg;
|
||||
this.secret = BigInteger.ZERO;
|
||||
ChannelImpl channels = new ChannelImpl();
|
||||
ByteEncoder<BigInteger> byteEncoder = new BigIntegerByteEncoder();
|
||||
while (!ids.isEmpty()) {
|
||||
id = ids.remove(random.nextInt(ids.size()));
|
||||
Channel channel = channels.getChannel(id);
|
||||
s = randomIntModQ(random);
|
||||
dkg = new meerkat.crypto.dkg.feldman.Protocol<BigInteger>(t, n, s, random, q, g, group, id,byteEncoder);
|
||||
dkgs[id - 1] = randomDKGUser(id,channel,dkg,random);
|
||||
threads[id - 1] = new Thread(dkgs[id - 1]);
|
||||
if(QUAL.contains(id)){
|
||||
this.secret = this.secret.add(s).mod(q);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public User<BigInteger> randomDKGUser(int id, Channel channel, Protocol<BigInteger> dkg, Random random){
|
||||
if (QUAL.size() <= t) {
|
||||
valids.add(id);
|
||||
QUAL.add(id);
|
||||
return new User<BigInteger>(dkg,channel);
|
||||
}else{
|
||||
int type = random.nextInt(3);
|
||||
switch (type){
|
||||
case 0:// regular
|
||||
valids.add(id);
|
||||
QUAL.add(id);
|
||||
return new User<BigInteger>(dkg,channel);
|
||||
case 1:// abort
|
||||
int abortStage = random.nextInt(2) + 1; // 1 or 2
|
||||
aborted.add(id);
|
||||
if (abortStage == 2){
|
||||
QUAL.add(id);
|
||||
}
|
||||
return new DKGUserImplAbort(dkg,channel,abortStage);
|
||||
case 2:// malicious
|
||||
malicious.add(id);
|
||||
Set<Integer> falls = DKGMaliciousUser.selectFallsRandomly(valids,random);
|
||||
Protocol<BigInteger> maliciousDKG = DKGMaliciousUser.generateMaliciousDKG(dkg,channel,random);
|
||||
return new DKGMaliciousUser(dkg,maliciousDKG,channel,falls);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BigInteger sampleGenerator(Random random){
|
||||
BigInteger ZERO = group.zero();
|
||||
BigInteger g;
|
||||
do {
|
||||
g = group.sample(random);
|
||||
} while (!g.equals(ZERO) && !group.multiply(g, q).equals(ZERO));
|
||||
return g;
|
||||
}
|
||||
|
||||
public BigInteger randomIntModQ(Random random){
|
||||
return new BigInteger(q.bitLength(), random).mod(q);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package meerkat.crypto.dkg.feldman;
|
||||
|
||||
import meerkat.comm.Channel;
|
||||
import meerkat.protobuf.DKG;
|
||||
|
||||
import static meerkat.crypto.dkg.comm.MessageUtils.createMessage;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 3/14/2016.
|
||||
*/
|
||||
public class DKGUserImplAbort<T> extends User<T> {
|
||||
|
||||
final int abortStage;
|
||||
int stage;
|
||||
public DKGUserImplAbort(Protocol<T> dkg, Channel channel, int abortStage) {
|
||||
super(dkg, channel);
|
||||
this.abortStage = abortStage;// 1 - 2
|
||||
this.stage = 1;
|
||||
}
|
||||
|
||||
|
||||
private void sendAbort(){
|
||||
channel.broadcastMessage(createMessage(DKG.Payload.Type.ABORT));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stage1() {
|
||||
if(stage < abortStage)
|
||||
super.stage1();
|
||||
else if(stage == abortStage){
|
||||
sendAbort();
|
||||
}
|
||||
stage++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stage2() {
|
||||
if(stage < abortStage)
|
||||
super.stage2();
|
||||
else if(stage == abortStage){
|
||||
sendAbort();
|
||||
}
|
||||
stage++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stage3() {
|
||||
if(stage < abortStage)
|
||||
super.stage3();
|
||||
else if(stage == abortStage){
|
||||
sendAbort();
|
||||
}
|
||||
stage++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stage4() {
|
||||
if(stage < abortStage)
|
||||
super.stage4();
|
||||
else if(stage == abortStage){
|
||||
sendAbort();
|
||||
}
|
||||
stage++;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package meerkat.crypto.dkg.gjkr;
|
||||
|
||||
import meerkat.comm.Channel;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 3/29/2016.
|
||||
*/
|
||||
public class SDKGMaliciousUserImpl<T> extends User<T> {
|
||||
|
||||
private final Protocol<T> maliciousSDKG;
|
||||
private final Set<Integer> falls;
|
||||
public SDKGMaliciousUserImpl(Protocol<T> sdkg, Protocol<T> maliciousSDKG
|
||||
, Channel channel, Set<Integer> falls) {
|
||||
super(sdkg, channel);
|
||||
this.falls = falls;
|
||||
this.maliciousSDKG = maliciousSDKG;
|
||||
maliciousSDKG.setParties(parties);
|
||||
}
|
||||
|
||||
public static<T> Protocol<T> generateMaliciousSDKG(Protocol<T> sdkg,Channel channel,Random random){
|
||||
BigInteger q = sdkg.getQ();
|
||||
BigInteger zi = new BigInteger(q.bitLength(), random).mod(q);
|
||||
Protocol<T> malicious = new Protocol<T>(sdkg.getT(),sdkg.getN(),zi,random,sdkg.getQ()
|
||||
,sdkg.getGenerator(),sdkg.getH(),sdkg.getGroup(),sdkg.getId(),sdkg.getEncoder());
|
||||
malicious.setChannel(channel);
|
||||
return malicious;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stage1() {
|
||||
sdkg.computeAndBroadcastVerificationValues();
|
||||
sendSecrets(); //insteadof crypto.sendSecrets(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stage3() {
|
||||
maliciousSDKG.answerAllComplainingPlayers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stage4(){
|
||||
//do nothing
|
||||
}
|
||||
|
||||
private void sendSecrets(){
|
||||
for (int j = 1; j <= n ; j++){
|
||||
if(j != id){
|
||||
if(falls.contains(j)){
|
||||
maliciousSDKG.sendSecret(j);
|
||||
}else {
|
||||
sdkg.sendSecret(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
package meerkat.crypto.dkg.gjkr;
|
||||
|
||||
import meerkat.comm.ChannelImpl;
|
||||
import meerkat.crypto.utils.Arithmetic;
|
||||
import meerkat.crypto.utils.concrete.Fp;
|
||||
import meerkat.comm.Channel;
|
||||
import meerkat.crypto.secretsharing.feldman.VerifiableSecretSharing;
|
||||
import meerkat.crypto.dkg.feldman.DKGMaliciousUser;
|
||||
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||
import meerkat.crypto.secretsharing.shamir.SecretSharing;
|
||||
import meerkat.crypto.utils.BigIntegerByteEncoder;
|
||||
import meerkat.crypto.utils.GenerateRandomPrime;
|
||||
import org.factcenter.qilin.primitives.Group;
|
||||
import org.factcenter.qilin.primitives.concrete.Zpstar;
|
||||
import org.factcenter.qilin.util.ByteEncoder;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.internal.runners.statements.Fail;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 3/29/2016.
|
||||
* TODO: Separate into multiple tests,
|
||||
* TODO: Make tests deterministic (using constant seed for random generator)
|
||||
*/
|
||||
public class SDKGTest {
|
||||
|
||||
private ExecutorService executorService = Executors.newCachedThreadPool();
|
||||
|
||||
final static int NUM_TESTS = 10;
|
||||
BigInteger p = GenerateRandomPrime.SafePrime100Bits;
|
||||
BigInteger q = p.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2));
|
||||
Group<BigInteger> group = new Zpstar(p);
|
||||
Arithmetic<BigInteger> arithmetic = new Fp(q);
|
||||
int t = 1;
|
||||
int n = 20;
|
||||
Random rand = new Random(1);
|
||||
|
||||
public void oneTest(Testable testable) throws Exception {
|
||||
for (int i = 0; i < testable.sdkgs.length ; i++){
|
||||
testable.futures[i] = executorService.submit(testable.sdkgs[i]);
|
||||
}
|
||||
for (int i = 0; i < testable.futures.length ; i++){
|
||||
testable.futures[i].get();
|
||||
}
|
||||
|
||||
// got the right public value
|
||||
BigInteger publicValue = group.multiply(testable.g,testable.secret);
|
||||
for (int i: testable.valids){
|
||||
assert (testable.sdkgs[i - 1].getPublicValue().equals(publicValue));
|
||||
}
|
||||
|
||||
// assert valid verification values
|
||||
BigInteger expected,verification;
|
||||
for (int i: testable.valids){
|
||||
expected = group.multiply(testable.g, testable.sdkgs[i - 1].getShare().y);
|
||||
verification = VerifiableSecretSharing.computeVerificationValue(i, testable.sdkgs[i - 1].getCommitments(), group);
|
||||
assert (expected.equals(verification));
|
||||
}
|
||||
|
||||
|
||||
// restore the secret from shares
|
||||
ArrayList<Polynomial.Point> sharesList = new ArrayList<Polynomial.Point>();
|
||||
|
||||
for (int i: testable.valids){
|
||||
sharesList.add(testable.sdkgs[i - 1].getShare());
|
||||
}
|
||||
Polynomial.Point[] shares = new Polynomial.Point[sharesList.size()];
|
||||
for (int i = 0; i < shares.length; i ++){
|
||||
shares[i] = sharesList.get(i);
|
||||
}
|
||||
|
||||
BigInteger calculatedSecret = SecretSharing.recoverSecret(shares,arithmetic);
|
||||
assert (calculatedSecret.equals(testable.secret));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
Testable testable;
|
||||
for (int i = 0; i < NUM_TESTS; i++) {
|
||||
testable = new Testable(n, t, group, q, rand);
|
||||
oneTest(testable);
|
||||
}
|
||||
}
|
||||
|
||||
static class Testable {
|
||||
Set<Integer> valids;
|
||||
Set<Integer> QUAL;
|
||||
Set<Integer> aborted;
|
||||
Set<Integer> malicious;
|
||||
User<BigInteger>[] sdkgs;
|
||||
Future<?>[] futures;
|
||||
BigInteger g;
|
||||
BigInteger h;
|
||||
BigInteger secret;
|
||||
Group<BigInteger> group;
|
||||
int n;
|
||||
int t;
|
||||
BigInteger q;
|
||||
Random random;
|
||||
ChannelImpl channels = new ChannelImpl();
|
||||
|
||||
public Testable(int n, int t, Group<BigInteger> group, BigInteger q, Random random) {
|
||||
this.n = n;
|
||||
this.t = t;
|
||||
this.group = group;
|
||||
this.q = q;
|
||||
this.random = random;
|
||||
this.sdkgs = new User[n];
|
||||
this.valids = new HashSet<Integer>();
|
||||
this.QUAL = new HashSet<Integer>();
|
||||
this.aborted = new HashSet<Integer>();
|
||||
this.malicious = new HashSet<Integer>();
|
||||
this.futures = new Future[n];
|
||||
this.g = sampleGenerator(random);
|
||||
this.h = group.multiply(g,randomIntModQ(random));
|
||||
ArrayList<Integer> ids = new ArrayList<Integer>();
|
||||
for (int id = 1; id<= n ; id++){
|
||||
ids.add(id);
|
||||
}
|
||||
int id;
|
||||
BigInteger s;
|
||||
Channel channel;
|
||||
Protocol<BigInteger> sdkg;
|
||||
this.secret = BigInteger.ZERO;
|
||||
ByteEncoder<BigInteger> encoder = new BigIntegerByteEncoder();
|
||||
while (!ids.isEmpty()) {
|
||||
id = ids.remove(random.nextInt(ids.size()));
|
||||
s = randomIntModQ(random);
|
||||
channel = channels.getChannel(id);
|
||||
sdkg = new Protocol<BigInteger>(t, n, s, random, q, g , h, group, id,encoder);
|
||||
sdkgs[id - 1] = randomSDKGUser(id,channel,sdkg);
|
||||
if(QUAL.contains(id)){
|
||||
this.secret = this.secret.add(s).mod(q);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum UserType {
|
||||
HONEST,
|
||||
FAILSTOP,
|
||||
MALICIOUS,
|
||||
}
|
||||
|
||||
public User<BigInteger> newSDKGUser(int id, Channel channel, Protocol<BigInteger> sdkg, UserType userType) {
|
||||
switch(userType) {
|
||||
case HONEST:
|
||||
valids.add(id);
|
||||
QUAL.add(id);
|
||||
return new User<BigInteger>(sdkg,channel);
|
||||
|
||||
case FAILSTOP:
|
||||
int abortStage = random.nextInt(3) + 1; // 1 or 2 or 3
|
||||
aborted.add(id);
|
||||
if (abortStage > 1){
|
||||
QUAL.add(id);
|
||||
}
|
||||
return new SDKGUserImplAbort(sdkg,channel,abortStage);
|
||||
|
||||
case MALICIOUS:
|
||||
malicious.add(id);
|
||||
Set<Integer> falls = DKGMaliciousUser.selectFallsRandomly(valids,random);
|
||||
Protocol<BigInteger> maliciousSDKG = SDKGMaliciousUserImpl.generateMaliciousSDKG(sdkg,channel,random);
|
||||
return new SDKGMaliciousUserImpl(sdkg,maliciousSDKG,channel,falls);
|
||||
|
||||
}
|
||||
fail("Unknown user type");
|
||||
return null;
|
||||
}
|
||||
|
||||
public User<BigInteger> randomSDKGUser(int id, Channel channel, Protocol<BigInteger> sdkg){
|
||||
if (QUAL.size() <= t) {
|
||||
return newSDKGUser(id, channel, sdkg, UserType.HONEST);
|
||||
} else {
|
||||
UserType type = UserType.values()[random.nextInt(UserType.values().length)];
|
||||
return newSDKGUser(id, channel, sdkg, type);
|
||||
}
|
||||
}
|
||||
|
||||
public BigInteger sampleGenerator(Random random){
|
||||
BigInteger ZERO = group.zero();
|
||||
BigInteger g;
|
||||
do {
|
||||
g = group.sample(random);
|
||||
} while (!g.equals(ZERO) && !group.multiply(g, q).equals(ZERO));
|
||||
return g;
|
||||
}
|
||||
|
||||
public BigInteger randomIntModQ(Random random){
|
||||
return new BigInteger(q.bitLength(), random).mod(q);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package meerkat.crypto.dkg.gjkr;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import meerkat.crypto.dkg.comm.MessageUtils;
|
||||
import meerkat.comm.Channel;
|
||||
import meerkat.protobuf.Comm;
|
||||
import meerkat.protobuf.DKG;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 3/14/2016.
|
||||
*/
|
||||
public class SDKGUserImplAbort<T> extends User<T> {
|
||||
|
||||
public static class AbortException extends RuntimeException {
|
||||
|
||||
}
|
||||
|
||||
final int abortStage;
|
||||
int stage;
|
||||
public SDKGUserImplAbort(Protocol<T> sdkg, Channel channel, int abortStage) {
|
||||
super(sdkg, channel);
|
||||
this.abortStage = abortStage;// 1 - 4
|
||||
this.stage = 1;
|
||||
}
|
||||
|
||||
private void abort(){
|
||||
//stopReceiver();
|
||||
channel.broadcastMessage(MessageUtils.createMessage(DKG.Payload.Type.ABORT));
|
||||
throw new AbortException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stage1() {
|
||||
if(stage < abortStage)
|
||||
super.stage1();
|
||||
else if(stage == abortStage){
|
||||
abort();
|
||||
}
|
||||
stage++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stage2() {
|
||||
if(stage < abortStage)
|
||||
super.stage2();
|
||||
else if(stage == abortStage){
|
||||
abort();
|
||||
}
|
||||
stage++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stage3() {
|
||||
if(stage < abortStage)
|
||||
super.stage3();
|
||||
else if(stage == abortStage){
|
||||
abort();
|
||||
}
|
||||
stage++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stage4() {
|
||||
if(stage < abortStage)
|
||||
super.stage4();
|
||||
else if(stage == abortStage){
|
||||
abort();
|
||||
}
|
||||
stage++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
super.run();
|
||||
} catch (AbortException e) {
|
||||
// Expected
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package meerkat.crypto.secretsharing.feldman;
|
||||
|
||||
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||
import org.factcenter.qilin.primitives.Group;
|
||||
import org.factcenter.qilin.primitives.concrete.Zpstar;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 1/29/2016.
|
||||
*/
|
||||
public class VerifiableSecretSharingTest {
|
||||
|
||||
|
||||
VerifiableSecretSharing[] verifiableSecretSharingArray;
|
||||
int tests = 1 << 10;
|
||||
Random random;
|
||||
|
||||
@Before
|
||||
public void settings(){
|
||||
BigInteger p = BigInteger.valueOf(2903);
|
||||
BigInteger q = p.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2));
|
||||
Zpstar zpstar = new Zpstar(p);
|
||||
random = new Random();
|
||||
BigInteger g;
|
||||
BigInteger ZERO = zpstar.zero();
|
||||
do{
|
||||
g = zpstar.sample(random);
|
||||
}while (!g.equals(ZERO) && !zpstar.multiply(g,q).equals(ZERO));// sample from QRZp*
|
||||
int t = 8;
|
||||
int n = 20;
|
||||
verifiableSecretSharingArray = new VerifiableSecretSharing[tests];
|
||||
for (int i = 0; i < verifiableSecretSharingArray.length; i++){
|
||||
verifiableSecretSharingArray[i] = new VerifiableSecretSharing(t,n
|
||||
,new BigInteger(q.bitLength(),random).mod(q),random,q,g,zpstar);
|
||||
}
|
||||
}
|
||||
|
||||
public void oneTest(VerifiableSecretSharing<BigInteger> verifiableSecretSharing) throws Exception {
|
||||
int n = verifiableSecretSharing.getN();
|
||||
Group<BigInteger> zpstar = verifiableSecretSharing.getGroup();
|
||||
BigInteger g = verifiableSecretSharing.getGenerator();
|
||||
Polynomial.Point[] shares = new Polynomial.Point[n];
|
||||
ArrayList<BigInteger> commitments = verifiableSecretSharing.getCommitmentsArrayList();
|
||||
BigInteger[] verifications = new BigInteger[n];
|
||||
for (int i = 1 ; i <= shares.length; i ++){
|
||||
shares[i - 1] = verifiableSecretSharing.getShare(i);
|
||||
verifications[i - 1] = VerifiableSecretSharing.computeVerificationValue(i,commitments,zpstar);
|
||||
}
|
||||
BigInteger expected;
|
||||
for (int i = 0 ; i < shares.length ; i++){
|
||||
expected = zpstar.multiply(g,shares[i].y);
|
||||
assert (expected.equals(verifications[i]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void secretSharingTest() throws Exception {
|
||||
for (int i = 0 ; i < verifiableSecretSharingArray.length; i ++){
|
||||
oneTest(verifiableSecretSharingArray[i]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package meerkat.crypto.secretsharing.shamir.PolynomialTests;
|
||||
import meerkat.crypto.utils.GenerateRandomPolynomial;
|
||||
import meerkat.crypto.utils.Z;
|
||||
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 1/27/2016.
|
||||
*/
|
||||
public class AddTest {
|
||||
|
||||
Polynomial[] arr1;
|
||||
Polynomial[] arr2;
|
||||
int tests = 1 << 12;
|
||||
int maxDegree = 15;
|
||||
int bits = 128;
|
||||
Random random;
|
||||
|
||||
@Before
|
||||
public void settings(){
|
||||
random = new Random();
|
||||
arr1 = new Polynomial[tests];
|
||||
arr2 = new Polynomial[tests];
|
||||
for (int i = 0; i < arr1.length; i++){
|
||||
arr1[i] = GenerateRandomPolynomial.generateRandomPolynomial(random.nextInt(maxDegree),bits,random,new Z());
|
||||
arr2[i] = GenerateRandomPolynomial.generateRandomPolynomial(random.nextInt(maxDegree),bits,random,new Z());
|
||||
}
|
||||
}
|
||||
|
||||
public void oneTest(Polynomial p1, Polynomial p2){
|
||||
Polynomial sum = p1.add(p2);
|
||||
BigInteger x = new BigInteger(bits,random);
|
||||
assert(sum.evaluate(x).equals(p1.evaluate(x).add(p2.evaluate(x))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addTest(){
|
||||
for (int i = 0 ; i < arr1.length; i ++){
|
||||
oneTest(arr1[i],arr2[i]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package meerkat.crypto.secretsharing.shamir.PolynomialTests;
|
||||
|
||||
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||
import meerkat.crypto.utils.Arithmetic;
|
||||
import meerkat.crypto.utils.concrete.Fp;
|
||||
import meerkat.crypto.utils.GenerateRandomPolynomial;
|
||||
import meerkat.crypto.utils.GenerateRandomPrime;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 1/27/2016.
|
||||
*/
|
||||
public class InterpolationTest {
|
||||
Polynomial[] polynomials;
|
||||
int tests = 1 << 10;
|
||||
int maxDegree = 15;
|
||||
int bits = 128;
|
||||
Random random;
|
||||
Polynomial.Point[][] pointsArrays;
|
||||
Arithmetic<BigInteger> arithmetic;
|
||||
BigInteger p = GenerateRandomPrime.SafePrime100Bits;
|
||||
|
||||
@Before
|
||||
public void settings(){
|
||||
random = new Random();
|
||||
polynomials = new Polynomial[tests];
|
||||
pointsArrays = new Polynomial.Point[tests][];
|
||||
arithmetic = new Fp(p);
|
||||
for (int i = 0; i < polynomials.length; i++){
|
||||
polynomials[i] = GenerateRandomPolynomial.generateRandomPolynomial(random.nextInt(maxDegree),bits,random,p);
|
||||
pointsArrays[i] = randomPoints(polynomials[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public Polynomial.Point[] randomPoints(Polynomial polynomial){
|
||||
Polynomial.Point[] points = new Polynomial.Point[polynomial.getDegree() + 1];
|
||||
BigInteger x;
|
||||
Set<BigInteger> set = new HashSet();
|
||||
for (int i = 0; i < points.length; i++){
|
||||
x = new BigInteger(bits,random).mod(p);
|
||||
if(set.contains(x)){
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
set.add(x);
|
||||
points[i] = new Polynomial.Point(x,polynomial);
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
public void oneTest(Polynomial p, Polynomial.Point[] points) throws Exception {
|
||||
Polynomial interpolation = Polynomial.interpolation(points,arithmetic);
|
||||
assert (p.compareTo(interpolation) == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interpolationTest() throws Exception {
|
||||
for (int i = 0; i < polynomials.length; i ++){
|
||||
oneTest(polynomials[i],pointsArrays[i]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package meerkat.crypto.secretsharing.shamir.PolynomialTests;
|
||||
|
||||
import meerkat.crypto.utils.GenerateRandomPolynomial;
|
||||
import meerkat.crypto.utils.Z;
|
||||
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 1/27/2016.
|
||||
*/
|
||||
public class MulByConstTest {
|
||||
|
||||
|
||||
Polynomial[] arr1;
|
||||
BigInteger[] arr2;
|
||||
int tests = 1 << 12;
|
||||
int maxDegree = 15;
|
||||
int bits = 128;
|
||||
Random random;
|
||||
|
||||
@Before
|
||||
public void settings(){
|
||||
random = new Random();
|
||||
arr1 = new Polynomial[tests];
|
||||
arr2 = new BigInteger[tests];
|
||||
for (int i = 0; i < arr1.length; i++){
|
||||
arr1[i] = GenerateRandomPolynomial.generateRandomPolynomial(random.nextInt(maxDegree),bits,random,new Z());
|
||||
arr2[i] = new BigInteger(bits,random);
|
||||
}
|
||||
}
|
||||
|
||||
public void oneTest(Polynomial p, BigInteger c){
|
||||
Polynomial product = p.mul(c);
|
||||
BigInteger x = new BigInteger(bits,random);
|
||||
assert(product.evaluate(x).equals(p.evaluate(x).multiply(c)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mulByConstTest(){
|
||||
for (int i = 0 ; i < arr1.length; i ++){
|
||||
oneTest(arr1[i],arr2[i]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package meerkat.crypto.secretsharing.shamir.PolynomialTests;
|
||||
|
||||
import meerkat.crypto.utils.GenerateRandomPolynomial;
|
||||
import meerkat.crypto.utils.Z;
|
||||
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 1/27/2016.
|
||||
*/
|
||||
public class MulTest {
|
||||
|
||||
|
||||
Polynomial[] arr1;
|
||||
Polynomial[] arr2;
|
||||
int tests = 1 << 12;
|
||||
int maxDegree = 15;
|
||||
int bits = 128;
|
||||
Random random;
|
||||
|
||||
@Before
|
||||
public void settings(){
|
||||
random = new Random();
|
||||
arr1 = new Polynomial[tests];
|
||||
arr2 = new Polynomial[tests];
|
||||
for (int i = 0; i < arr1.length; i++){
|
||||
arr1[i] = GenerateRandomPolynomial.generateRandomPolynomial(random.nextInt(maxDegree),bits,random,new Z());
|
||||
arr2[i] = GenerateRandomPolynomial.generateRandomPolynomial(random.nextInt(maxDegree),bits,random,new Z());
|
||||
}
|
||||
}
|
||||
|
||||
public void oneTest(Polynomial p1, Polynomial p2){
|
||||
Polynomial product = p1.mul(p2);
|
||||
BigInteger x = new BigInteger(bits,random);
|
||||
assert(product.evaluate(x).equals(p1.evaluate(x).multiply(p2.evaluate(x))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mulTest(){
|
||||
for (int i = 0 ; i < arr1.length; i ++){
|
||||
oneTest(arr1[i],arr2[i]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package meerkat.crypto.secretsharing.shamir;
|
||||
|
||||
import meerkat.crypto.utils.concrete.Fp;
|
||||
import meerkat.crypto.utils.GenerateRandomPrime;
|
||||
import org.factcenter.qilin.primitives.CyclicGroup;
|
||||
import org.factcenter.qilin.primitives.concrete.Zn;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Created by Tzlil on 1/29/2016.
|
||||
*/
|
||||
public class SecretSharingTest {
|
||||
|
||||
SecretSharing[] secretSharingArray;
|
||||
BigInteger[] secrets;
|
||||
CyclicGroup<BigInteger> group;
|
||||
int tests = 1 << 10;
|
||||
Random random;
|
||||
BigInteger p = GenerateRandomPrime.SafePrime100Bits;
|
||||
BigInteger q = p.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2));
|
||||
|
||||
@Before
|
||||
public void settings(){
|
||||
group = new Zn(q);
|
||||
int t = 9;
|
||||
int n = 20;
|
||||
random = new Random();
|
||||
secretSharingArray = new SecretSharing[tests];
|
||||
secrets = new BigInteger[tests];
|
||||
|
||||
for (int i = 0; i < secretSharingArray.length; i++){
|
||||
secrets[i] = group.sample(random);
|
||||
secretSharingArray[i] = new SecretSharing(t,n,secrets[i],random,q);
|
||||
}
|
||||
}
|
||||
|
||||
public void oneTest(SecretSharing secretSharing, BigInteger secret) throws Exception {
|
||||
int t = secretSharing.getT();
|
||||
int n = secretSharing.getN();
|
||||
Polynomial.Point[] shares = new Polynomial.Point[t + 1];
|
||||
List<Integer> indexes = new ArrayList<Integer>(n);
|
||||
for (int i = 1 ; i <= n; i ++){
|
||||
indexes.add(i);
|
||||
}
|
||||
for (int i = 0 ; i < shares.length ; i++){
|
||||
shares[i] = secretSharing.getShare(indexes.remove(random.nextInt(indexes.size())));
|
||||
}
|
||||
BigInteger calculated = SecretSharing.recoverSecret(shares,new Fp(q));
|
||||
assert (secret.equals(calculated));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void secretSharingTest() throws Exception {
|
||||
for (int i = 0 ; i < secretSharingArray.length; i ++){
|
||||
oneTest(secretSharingArray[i],secrets[i]);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue