mixer
Tal Moran 2016-04-05 11:32:07 +03:00
commit 6d6e4748b7
48 changed files with 2776 additions and 777 deletions

View File

@ -1,5 +1,4 @@
subprojects { proj ->
proj.afterEvaluate {
// Used to generate initial maven-dir layout

View File

@ -0,0 +1,82 @@
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);
}
}

View File

@ -0,0 +1,29 @@
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;
}
}

View File

@ -0,0 +1,217 @@
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");
}
}
}

View File

@ -0,0 +1,161 @@
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));
// }
}

View File

@ -0,0 +1,131 @@
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());
}
}
}

View File

@ -0,0 +1,25 @@
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;
}
}

View File

@ -0,0 +1,38 @@
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);
}
}

View File

@ -0,0 +1,46 @@
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);
}
}

View File

@ -0,0 +1,38 @@
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);
}
}

View File

@ -0,0 +1,214 @@
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;
}
}
}

View File

@ -2,9 +2,10 @@
plugins {
id "us.kirchmeier.capsule" version "1.0.1"
id 'com.google.protobuf' version '0.7.0'
id "org.akhikhl.gretty" version "1.2.4"
id 'org.akhikhl.gretty' version "1.2.4"
}
apply plugin: 'org.akhikhl.gretty'
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
@ -44,8 +45,15 @@ dependencies {
// Jersey for RESTful API
compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.22.+'
// JDBC connections
compile 'org.springframework:spring-jdbc:4.2.+'
compile 'org.xerial:sqlite-jdbc:3.7.+'
compile 'mysql:mysql-connector-java:5.1.+'
compile 'com.h2database:h2:1.0.+'
// Servlets
compile 'javax.servlet:javax.servlet-api:3.0.+'
// Logging
compile 'org.slf4j:slf4j-api:1.7.7'
@ -65,16 +73,22 @@ dependencies {
test {
exclude '**/*SQLite*Test*'
exclude '**/*H2*Test*'
exclude '**/*MySql*Test'
exclude '**/*IntegrationTest*'
}
task debugIntegrationTest(type: Test){
include '**/*IntegrationTest*'
debug = true
task dbTest(type: Test) {
include '**/*H2*Test*'
include '**/*MySql*Test'
}
task integrationTest(type: Test) {
include '**/*IntegrationTest*'
// debug = true
outputs.upToDateWhen { false }
}
gretty {
@ -82,6 +96,7 @@ gretty {
contextPath = '/'
integrationTestTask = 'integrationTest'
loggingLevel = 'TRACE'
debugPort = 5006
}

View File

@ -1,104 +0,0 @@
package meerkat.bulletinboard.httpserver;
import java.io.File;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import meerkat.bulletinboard.BulletinBoardServer;
import meerkat.bulletinboard.sqlserver.SQLiteBulletinBoardServer;
import meerkat.comm.CommunicationException;
import meerkat.protobuf.BulletinBoardAPI.*;
public class BulletinBoardHttpServer extends HttpServlet {
public final static File DEFAULT_MEERKAT_DB = new File("local-instances/meerkat.db");
/**
* Auto-generated UID.
*/
private static final long serialVersionUID = -1263665607729456165L;
BulletinBoardServer bbs;
@Override
public void init(ServletConfig config) throws ServletException {
//TODO: Make this generic
bbs = new SQLiteBulletinBoardServer();
try {
bbs.init(DEFAULT_MEERKAT_DB);
} catch (CommunicationException e) {
// TODO Log error
throw new ServletException("Servlet failed to initialize: " + e.getMessage());
}
}
/**
* This procedure handles (POST) requests to post messages to the Bulletin Board.
*/
@Override
protected void doPost( HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
BulletinBoardMessage message;
try{
message = BulletinBoardMessage.newBuilder()
.mergeFrom(request.getInputStream())
.build();
} catch(Exception e){
//TODO: Log invalid request
return;
}
try {
bbs.postMessage(message);
} catch (CommunicationException e) {
// TODO Log DB communication error
}
}
/**
* This procedure handles (GET) requests which request data from the Bulletin Board.
*/
@Override
protected void doGet( HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
BulletinBoardMessage message;
try{
message = BulletinBoardMessage.newBuilder()
.mergeFrom(request.getInputStream())
.build();
} catch(Exception e){
//TODO: Log invalid request
return;
}
try {
bbs.postMessage(message);
} catch (CommunicationException e) {
// TODO Log DB communication error
}
}
@Override
public void destroy() {
try {
bbs.close();
} catch (CommunicationException e) {
// TODO Log DB communication error
}
}
}

View File

@ -1,18 +1,14 @@
package meerkat.bulletinboard.sqlserver;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.sql.*;
import java.util.*;
import com.google.protobuf.ProtocolStringList;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import meerkat.bulletinboard.BulletinBoardServer;
import meerkat.bulletinboard.sqlserver.mappers.EntryNumMapper;
import meerkat.bulletinboard.sqlserver.mappers.MessageMapper;
import meerkat.bulletinboard.sqlserver.mappers.SignatureMapper;
import meerkat.comm.CommunicationException;
import meerkat.protobuf.BulletinBoardAPI.*;
import meerkat.protobuf.Crypto.Signature;
@ -20,9 +16,182 @@ import meerkat.protobuf.Crypto.SignatureVerificationKey;
import meerkat.crypto.Digest;
import meerkat.crypto.concrete.SHA256Digest;
public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
protected Connection connection;
import javax.sql.DataSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
/**
* This is a generic SQL implementation of the BulletinBoardServer API.
*/
public class BulletinBoardSQLServer implements BulletinBoardServer{
/**
* This interface provides the required implementation-specific data to enable an access to an actual SQL server.
* It accounts for differences in languages between SQL DB types and for the different addresses needed to access them.
*/
public interface SQLQueryProvider {
/**
* Allowed query types.
* Note that each query returned has to comply with the parameter names specified ny getParamNames
*/
public static enum QueryType {
FIND_MSG_ID(new String[] {"MsgId"}),
INSERT_MSG(new String[] {"MsgId","Msg"}),
INSERT_NEW_TAG(new String[] {"Tag"}),
CONNECT_TAG(new String[] {"EntryNum","Tag"}),
ADD_SIGNATURE(new String[] {"EntryNum","SignerId","Signature"}),
GET_SIGNATURES(new String[] {"EntryNum"}),
GET_MESSAGES(new String[] {});
private String[] paramNames;
private QueryType(String[] paramNames) {
this.paramNames = paramNames;
}
public String[] getParamNames() {
return paramNames;
}
}
/**
* This enum provides the standard translation between a filter type and the corresponding parameter name in the SQL query
*/
public static enum FilterTypeParam {
ENTRY_NUM("EntryNum", Types.INTEGER),
MSG_ID("MsgId", Types.BLOB),
SIGNER_ID("SignerId", Types.BLOB),
TAG("Tag", Types.VARCHAR),
LIMIT("Limit", Types.INTEGER);
private FilterTypeParam(String paramName, int paramType) {
this.paramName = paramName;
this.paramType = paramType;
}
private String paramName;
private int paramType;
public static FilterTypeParam getFilterTypeParamName(FilterType filterType) {
switch (filterType) {
case MSG_ID:
return MSG_ID;
case EXACT_ENTRY: // Go through
case MAX_ENTRY:
return ENTRY_NUM;
case SIGNER_ID:
return SIGNER_ID;
case TAG:
return TAG;
case MAX_MESSAGES:
return LIMIT;
default:
return null;
}
}
public String getParamName() {
return paramName;
}
public int getParamType() {
return paramType;
}
}
/**
* This function translates a QueryType into an actual SQL query.
* @param queryType is the type of query requested
* @return a string representation of the query for the specific type of SQL database implemented.
*/
public String getSQLString(QueryType queryType) throws IllegalArgumentException;
/**
* Used to retrieve a condition to add to an SQL statement that will make the result comply with the filter type
* @param filterType is the filter type
* @param serialNum is a unique number used to identify the condition variables from other condition instances
* @return The SQL string for the condition
* @throws IllegalArgumentException if the filter type used is not supported
*/
public String getCondition(FilterType filterType, int serialNum) throws IllegalArgumentException;
public String getConditionParamTypeName(FilterType filterType) throws IllegalArgumentException;
/**
* @return the string needed in order to connect to the DB.
*/
public DataSource getDataSource();
/**
* This is used to get a list of queries that together create the schema needed for the DB.
* Note that these queries should not assume anything about the current state of the DB.
* In particular: they should not erase any existing tables and/or entries.
* @return The list of queries.
*/
public List<String> getSchemaCreationCommands();
/**
* This is used to get a list of queries that together delete the schema needed for the DB.
* This is useful primarily for tests, in which we want to make sure we start with a clean DB.
* @return The list of queries.
*/
public List<String> getSchemaDeletionCommands();
}
private Object getParam(MessageFilter messageFilter) {
switch (messageFilter.getType()) {
case MSG_ID: // Go through
case SIGNER_ID:
return messageFilter.getId().toByteArray();
case EXACT_ENTRY: // Go through
case MAX_ENTRY:
return messageFilter.getEntry();
case TAG:
return messageFilter.getTag();
case MAX_MESSAGES:
return messageFilter.getMaxMessages();
default:
return null;
}
}
/**
* This class implements a comparator for the MessageFilter class
* The comparison is done solely by comparing the type of the filter
* This is used to sort the filters by type
*/
public class FilterTypeComparator implements Comparator<MessageFilter> {
@Override
public int compare(MessageFilter filter1, MessageFilter filter2) {
return filter1.getTypeValue() - filter2.getTypeValue();
}
}
protected SQLQueryProvider sqlQueryProvider;
protected NamedParameterJdbcTemplate jdbcTemplate;
protected Digest digest;
@ -31,18 +200,47 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
protected List<List<SignatureVerificationKey>> pollingCommitteeSignatureVerificationKeyArray;
protected int minCommiteeSignatures;
/**
* This method initializes the signatures but does not implement the DB connection.
* Any full (non-abstract) extension of this class should
* 1. Establish a DB connection, and
* 2. Call this procedure
* This constructor sets the type of SQL language in use.
* @param sqlQueryProvider is the provider of the SQL query strings required for actual operation of the server.
*/
public BulletinBoardSQLServer(SQLQueryProvider sqlQueryProvider) {
this.sqlQueryProvider = sqlQueryProvider;
}
/**
* This method creates the schema in the given DB to prepare for future transactions
* It does not assume anything about the current state of the database
* @throws SQLException
*/
private void createSchema() throws SQLException {
final int TIMEOUT = 20;
for (String command : sqlQueryProvider.getSchemaCreationCommands()) {
jdbcTemplate.update(command,(Map) null);
}
}
/**
* This method initializes the signatures, connects to the DB and creates the schema (if required).
*/
@Override
public void init(File meerkatDB) throws CommunicationException {
public void init(String meerkatDB) throws CommunicationException {
// TODO write signature reading part.
digest = new SHA256Digest();
jdbcTemplate = new NamedParameterJdbcTemplate(sqlQueryProvider.getDataSource());
try {
createSchema();
} catch (SQLException e) {
throw new CommunicationException("Couldn't create schema " + e.getMessage());
}
}
/**
@ -62,7 +260,21 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
* This procedure makes sure that all tags in the given list have an entry in the tags table.
* @param tags
*/
protected abstract void insertNewTags(String[] tags) throws SQLException;
protected void insertNewTags(String[] tags) throws SQLException {
String sql;
sql = sqlQueryProvider.getSQLString(SQLQueryProvider.QueryType.INSERT_NEW_TAG);
Map namedParameters[] = new HashMap[tags.length];
for (int i = 0 ; i < tags.length ; i++){
namedParameters[i] = new HashMap();
namedParameters[i].put("Tag", tags[i]);
}
jdbcTemplate.batchUpdate(sql, namedParameters);
}
/**
* This procedure is used to convert a boolean to a BoolMsg.
@ -74,17 +286,17 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
.setValue(b)
.build();
}
@Override
public BoolMsg postMessage(BulletinBoardMessage msg) throws CommunicationException {
if (!verifyMessage(msg)) {
return boolToBoolMsg(false);
}
PreparedStatement pstmt;
ResultSet rs;
String sql;
Map[] namedParameterArray;
byte[] msgID;
long entryNum;
@ -102,36 +314,28 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
msgID = digest.digest();
// Add message to table if needed and store entry number of message.
try {
sql = "SELECT EntryNum From MsgTable WHERE MsgId = ?";
pstmt = connection.prepareStatement(sql);
pstmt.setBytes(1, msgID);
rs = pstmt.executeQuery();
if (rs.next()){
entryNum = rs.getLong(1);
} else{
sql = "INSERT INTO MsgTable (MsgId, Msg) VALUES(?,?)";
pstmt = connection.prepareStatement(sql);
pstmt.setBytes(1, msgID);
pstmt.setBytes(2, msg.getMsg().toByteArray());
pstmt.executeUpdate();
rs = pstmt.getGeneratedKeys();
rs.next();
entryNum = rs.getLong(1);
}
pstmt.close();
} catch (SQLException e) {
throw new CommunicationException("Error inserting into MsgTable: " + e.getMessage());
sql = sqlQueryProvider.getSQLString(SQLQueryProvider.QueryType.FIND_MSG_ID);
Map namedParameters = new HashMap();
namedParameters.put("MsgId",msgID);
List<Long> entryNums = jdbcTemplate.query(sql, new MapSqlParameterSource(namedParameters), new EntryNumMapper());
if (entryNums.size() > 0){
entryNum = entryNums.get(0);
} else{
sql = sqlQueryProvider.getSQLString(SQLQueryProvider.QueryType.INSERT_MSG);
namedParameters.put("Msg", msg.getMsg().toByteArray());
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(sql,new MapSqlParameterSource(namedParameters),keyHolder);
entryNum = keyHolder.getKey().longValue();
}
// Retrieve tags and store new ones in tag table.
@ -149,24 +353,18 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
}
// Connect message to tags.
try{
sql = "INSERT OR IGNORE INTO MsgTagTable (TagId, EntryNum) SELECT TagTable.TagId, ? AS EntryNum FROM TagTable WHERE Tag = ?";
pstmt = connection.prepareStatement(sql);
pstmt.setLong(1, entryNum);
for (String tag : tags){
pstmt.setString(2, tag);
pstmt.addBatch();
}
pstmt.executeBatch();
pstmt.close();
} catch (SQLException e) {
throw new CommunicationException("Error Linking tags: " + e.getMessage());
sql = sqlQueryProvider.getSQLString(SQLQueryProvider.QueryType.CONNECT_TAG);
namedParameterArray = new HashMap[tags.length];
for (int i = 0 ; i < tags.length ; i++) {
namedParameterArray[i] = new HashMap();
namedParameterArray[i].put("EntryNum", entryNum);
namedParameterArray[i].put("Tag", tags[i]);
}
jdbcTemplate.batchUpdate(sql, namedParameterArray);
// Retrieve signatures.
@ -175,64 +373,112 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
signatures = signatureList.toArray(signatures);
// Connect message to signatures.
try{
sql = "INSERT OR IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (?,?,?)";
pstmt = connection.prepareStatement(sql);
pstmt.setLong(1, entryNum);
for (Signature sig : signatures){
pstmt.setBytes(2, sig.getSignerId().toByteArray());
pstmt.setBytes(3, sig.toByteArray());
pstmt.addBatch();
}
pstmt.executeBatch();
pstmt.close();
} catch (SQLException e) {
throw new CommunicationException("Error Linking tags: " + e.getMessage());
sql = sqlQueryProvider.getSQLString(SQLQueryProvider.QueryType.ADD_SIGNATURE);
namedParameterArray = new HashMap[signatures.length];
for (int i = 0 ; i < signatures.length ; i++) {
namedParameterArray[i] = new HashMap();
namedParameterArray[i].put("EntryNum", entryNum);
namedParameterArray[i].put("SignerId", signatures[i].getSignerId().toByteArray());
namedParameterArray[i].put("Signature", signatures[i].toByteArray());
}
jdbcTemplate.batchUpdate(sql,namedParameterArray);
return boolToBoolMsg(true);
}
public String testPrint(){
String s = "";
try {
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery("select * from MsgTable");
while (rs.next()) {
// read the result set
s += "entry = " + rs.getInt("EntryNum") + " \n";
s += "id = " + Arrays.toString(rs.getBytes("MsgId")) + " \n";
s += "msg = " + Arrays.toString(rs.getBytes("Msg")) + " \n";
s += "signer ID = " + Arrays.toString(rs.getBytes("SignerId")) + "\t\n<BR>";
@Override
public BulletinBoardMessageList readMessages(MessageFilterList filterList) throws CommunicationException {
BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder();
// SQL length is roughly 50 characters per filter + 50 for the query itself
StringBuilder sqlBuilder = new StringBuilder(50 * (filterList.getFilterCount() + 1));
MapSqlParameterSource namedParameters;
int paramNum;
MessageMapper messageMapper = new MessageMapper();
SignatureMapper signatureMapper = new SignatureMapper();
List<MessageFilter> filters = new ArrayList<MessageFilter>(filterList.getFilterList());
boolean isFirstFilter = true;
Collections.sort(filters, new FilterTypeComparator());
// Check if Tag/Signature tables are required for filtering purposes
sqlBuilder.append(sqlQueryProvider.getSQLString(SQLQueryProvider.QueryType.GET_MESSAGES));
// Add conditions
namedParameters = new MapSqlParameterSource();
if (!filters.isEmpty()) {
sqlBuilder.append(" WHERE ");
for (paramNum = 0 ; paramNum < filters.size() ; paramNum++) {
MessageFilter filter = filters.get(paramNum);
if (filter.getType().getNumber() != FilterType.MAX_MESSAGES_VALUE) {
if (isFirstFilter) {
isFirstFilter = false;
} else {
sqlBuilder.append(" AND ");
}
}
sqlBuilder.append(sqlQueryProvider.getCondition(filter.getType(), paramNum));
SQLQueryProvider.FilterTypeParam filterTypeParam = SQLQueryProvider.FilterTypeParam.getFilterTypeParamName(filter.getType());
namedParameters.addValue(
filterTypeParam.getParamName() + Integer.toString(paramNum),
getParam(filter),
filterTypeParam.getParamType(),
sqlQueryProvider.getConditionParamTypeName(filter.getType()));
}
rs = statement.executeQuery("select * from TagTable");
while (rs.next()) {
// read the result set
s += "Tag = " + rs.getString("Tag") + " \n";
s += "TagId = " + rs.getInt("TagId") + "\t\n<BR>";
}
rs = statement.executeQuery("select * from MsgTagTable");
while (rs.next()) {
// read the result set
s += "MsgId = " + Arrays.toString(rs.getBytes("MsgId")) + " \n";
s += "TagId = " + rs.getInt("TagId") + "\t\n<BR>";
}
} catch(SQLException e){
s += "Error reading from DB";
}
return s;
// Run query
List<BulletinBoardMessage.Builder> msgBuilders = jdbcTemplate.query(sqlBuilder.toString(), namedParameters, messageMapper);
// Compile list of messages
for (BulletinBoardMessage.Builder msgBuilder : msgBuilders) {
// Retrieve signatures
namedParameters = new MapSqlParameterSource();
namedParameters.addValue("EntryNum", msgBuilder.getEntryNum());
List<Signature> signatures = jdbcTemplate.query(
sqlQueryProvider.getSQLString(SQLQueryProvider.QueryType.GET_SIGNATURES),
namedParameters,
signatureMapper);
// Append signatures
msgBuilder.addAllSig(signatures);
// Finalize message and add to message list.
resultListBuilder.addMessage(msgBuilder.build());
}
//Combine results and return.
return resultListBuilder.build();
}
@Override
public void close() {}
}

View File

@ -0,0 +1,164 @@
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;
}
}

View File

@ -0,0 +1,148 @@
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;
}
}

View File

@ -1,267 +0,0 @@
package meerkat.bulletinboard.sqlserver;
import java.io.File;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.protobuf.BulletinBoardAPI.*;
import meerkat.protobuf.Crypto.Signature;
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
import meerkat.comm.CommunicationException;
public class SQLiteBulletinBoardServer extends BulletinBoardSQLServer {
protected static final int TIMEOUT = 20;
/**
* This procedure initializes:
* 1. The database connection
* 2. The database tables (if they do not yet exist).
*/
private void createSchema() throws SQLException {
Statement statement = connection.createStatement();
statement.setQueryTimeout(TIMEOUT);
statement.executeUpdate("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INTEGER PRIMARY KEY, MsgId BLOB UNIQUE, Msg BLOB)");
statement.executeUpdate("CREATE TABLE IF NOT EXISTS TagTable (TagId INTEGER PRIMARY KEY, Tag varchar(50) UNIQUE)");
statement.executeUpdate("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))");
statement.executeUpdate("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum BLOB, SignerId BLOB, Signature BLOB UNIQUE, FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))");
statement.executeUpdate("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)");
statement.close();
}
@Override
public void init(File meerkatDB) throws CommunicationException {
try{
String dbString = "jdbc:sqlite:" + meerkatDB.getPath();
connection = DriverManager.getConnection(dbString);
createSchema();
super.init(meerkatDB);
} catch (SQLException e) {
throw new CommunicationException("Couldn't form a connection with the database" + e.getMessage());
}
}
public void close() throws CommunicationException{
try{
connection.close();
} catch (SQLException e) {
throw new CommunicationException("Couldn't close connection to the database");
}
}
@Override
protected void insertNewTags(String[] tags) throws SQLException {
PreparedStatement pstmt;
String sql;
try {
sql = "INSERT OR IGNORE INTO TagTable(Tag) VALUES (?)";
pstmt = connection.prepareStatement(sql);
for (String tag : tags){
pstmt.setString(1, tag);
pstmt.addBatch();
}
pstmt.executeBatch();
pstmt.close();
} catch (SQLException e){
throw new SQLException("Error adding new tags to table: " + e.getMessage());
}
}
@Override
public BoolMsg postMessage(BulletinBoardMessage msg) throws CommunicationException {
return super.postMessage(msg);
}
@Override
public BulletinBoardMessageList readMessages(MessageFilterList filterList) throws CommunicationException{
PreparedStatement pstmt;
ResultSet messages, signatures;
long entryNum;
BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder();
BulletinBoardMessage.Builder messageBuilder;
String sql;
String sqlSuffix = "";
List<MessageFilter> filters = filterList.getFilterList();
int i;
boolean tagsRequired = false;
boolean signaturesRequired = false;
boolean isFirstFilter = true;
// Check if Tag/Signature tables are required for filtering purposes.
for (MessageFilter filter : filters){
if (filter.getType() == FilterType.TAG){
tagsRequired = true;
} else if (filter.getType() == FilterType.SIGNER_ID){
signaturesRequired = true;
}
}
sql = "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
if (tagsRequired){
sql += " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum";
sql += " INNER JOIN TagTable ON TagTable.TagId = MsgTagTable.TagId";
}
if (signaturesRequired){
sql += " INNER JOIN SignatureTable ON SignatureTable.EntryNum = MsgTable.EntryNum";
}
// Add conditions.
if (!filters.isEmpty()){
sql += " WHERE";
for (MessageFilter filter : filters){
if (filter.getType().getNumber() != FilterType.MAX_MESSAGES_VALUE){
if (isFirstFilter){
isFirstFilter = false;
} else{
sql += " AND";
}
}
switch (filter.getType().getNumber()){
case FilterType.EXACT_ENTRY_VALUE:
sql += " MsgTable.EntryNum = ?";
break;
case FilterType.MAX_ENTRY_VALUE:
sql += " MsgTable.EntryNum <= ?";
break;
case FilterType.MAX_MESSAGES_VALUE:
sqlSuffix += " LIMIT = ?";
break;
case FilterType.MSG_ID_VALUE:
sql += " MsgTableMsgId = ?";
break;
case FilterType.SIGNER_ID_VALUE:
sql += " SignatureTable.SignerId = ?";
break;
case FilterType.TAG_VALUE:
sql += " TagTable.Tag = ?";
break;
}
}
sql += sqlSuffix;
}
// Make query.
try {
pstmt = connection.prepareStatement(sql);
// Specify values for filters.
i = 1;
for (MessageFilter filter : filters){
switch (filter.getType().getNumber()){
case FilterType.EXACT_ENTRY_VALUE: // Go through.
case FilterType.MAX_ENTRY_VALUE:
pstmt.setLong(i, filter.getEntry());
i++;
break;
case FilterType.MSG_ID_VALUE: // Go through.
case FilterType.SIGNER_ID_VALUE:
pstmt.setBytes(i, filter.getId().toByteArray());
i++;
break;
case FilterType.TAG_VALUE:
pstmt.setString(i, filter.getTag());
break;
// The max-messages condition is applied as a suffix. Therefore, it is treated differently.
case FilterType.MAX_MESSAGES_VALUE:
pstmt.setLong(filters.size(), filter.getMaxMessages());
break;
}
}
// Run query.
messages = pstmt.executeQuery();
// Compile list of messages.
sql = "SELECT Signature FROM SignatureTable WHERE EntryNum = ?";
pstmt = connection.prepareStatement(sql);
while (messages.next()){
// Get entry number and retrieve signatures.
entryNum = messages.getLong(1);
pstmt.setLong(1, entryNum);
signatures = pstmt.executeQuery();
// Create message and append signatures.
messageBuilder = BulletinBoardMessage.newBuilder()
.setEntryNum(entryNum)
.setMsg(UnsignedBulletinBoardMessage.parseFrom(messages.getBytes(2)));
while (signatures.next()){
messageBuilder.addSig(Signature.parseFrom(signatures.getBytes(1)));
}
// Finalize message and add to message list.
resultListBuilder.addMessage(messageBuilder.build());
}
pstmt.close();
} catch (SQLException e){
throw new CommunicationException("Error reading messages from DB: " + e.getMessage());
} catch (InvalidProtocolBufferException e) {
throw new CommunicationException("Invalid data from DB: " + e.getMessage());
}
//Combine results and return.
return resultListBuilder.build();
}
}

View File

@ -0,0 +1,123 @@
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;
}
}

View File

@ -0,0 +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);
}
}

View File

@ -0,0 +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;
}
}

View File

@ -0,0 +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);
}
}
}

View File

@ -1,16 +1,21 @@
package meerkat.bulletinboard.webapp;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
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.SQLiteBulletinBoardServer;
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;
@ -18,42 +23,89 @@ import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessageList;
import meerkat.protobuf.BulletinBoardAPI.MessageFilterList;
import meerkat.rest.Constants;
import java.io.File;
@Path("/sqlserver")
public class BulletinBoardWebApp implements BulletinBoardServer {
@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;
@PostConstruct
/**
* 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(File meerkatDB) throws CommunicationException {
bulletinBoard = new SQLiteBulletinBoardServer();
public void init(String meerkatDB) throws CommunicationException {
bulletinBoard.init(meerkatDB);
}
@Path("postmessage")
@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("readmessages")
@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
@PreDestroy
public void close() throws CommunicationException {
bulletinBoard.close();
public void close(){
try {
bulletinBoard.close();
} catch (CommunicationException e) {
System.err.println(e.getMessage());
}
}
@GET
@ -62,4 +114,11 @@ public class BulletinBoardWebApp implements BulletinBoardServer {
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();
}
}

View File

@ -14,4 +14,25 @@
<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>

View File

@ -19,19 +19,17 @@ import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
public class SQLiteServerIntegrationTest {
public class BulletinBoardSQLServerIntegrationTest {
private static String PROP_GETTY_URL = "gretty.httpBaseURI";
private static String DEFAULT_BASE_URL = "localhost:8081";
private static String DEFAULT_BASE_URL = "http://localhost:8081";
private static String BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL);
private static String SQL_SERVER_POST = "sqlserver/postmessage";
private static String SQL_SERVER_GET = "sqlserver/readmessages";
Client client;
// Connection connection;
@Before
public void setup() throws Exception {
System.err.println("Registering client");
client = ClientBuilder.newClient();
client.register(ProtobufMessageBodyReader.class);
client.register(ProtobufMessageBodyWriter.class);
@ -54,17 +52,11 @@ public class SQLiteServerIntegrationTest {
MessageFilterList filterList;
BulletinBoardMessageList msgList;
// try{
// connection = DriverManager.getConnection("jdbc:sqlite:d:/arbel/projects/meerkat-java/bulletin-board-server/local-instances/meerkat.db");
// } catch (SQLException e) {
// System.err.println(e.getMessage());
// assert false;
// }
// Test writing mechanism
System.err.println("******** Testing: " + SQL_SERVER_POST);
webTarget = client.target(BASE_URL).path(SQL_SERVER_POST);
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()
@ -109,9 +101,8 @@ public class SQLiteServerIntegrationTest {
// Test reading mechanism
System.err.println("******** Testing: " + SQL_SERVER_GET);
webTarget = client.target(BASE_URL).path(SQL_SERVER_GET);
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()
@ -120,47 +111,7 @@ public class SQLiteServerIntegrationTest {
.build()
)
.build();
// String sql = "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable INNER JOIN SignatureTable ON SignatureTable.EntryNum = MsgTable.EntryNum WHERE SignatureTable.SignerId = ?";
// PreparedStatement pstmt = connection.prepareStatement(sql);
// int i=1;
// for (MessageFilter filter : filterList.getFilterList()){
//
// switch (filter.getType().getNumber()){
//
// case FilterType.EXACT_ENTRY_VALUE: // Go through.
// case FilterType.MAX_ENTRY_VALUE:
// pstmt.setLong(i, filter.getEntry());
// i++;
// break;
//
// case FilterType.MSG_ID_VALUE: // Go through.
// case FilterType.SIGNER_ID_VALUE:
// pstmt.setBytes(i, filter.getId().toByteArray());
// i++;
// break;
//
// case FilterType.TAG_VALUE:
// pstmt.setString(i, filter.getTag());
// break;
//
// // The max-messages condition is applied as a suffix. Therefore, it is treated differently.
// case FilterType.MAX_MESSAGES_VALUE:
// pstmt.setLong(filterList.getFilterList().size(), filter.getMaxMessages());
// break;
//
// }
// }
// ResultSet rs = pstmt.executeQuery();
//
// i = 0;
// while (rs.next()){
// i++;
// assert rs.getBytes(2)
// }
// System.err.println("Local DB size = " + i);
// pstmt.close();
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF));
System.err.println(response);
msgList = response.readEntity(BulletinBoardMessageList.class);

View File

@ -1,23 +0,0 @@
package meerkat.bulletinboard;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Test;
import meerkat.bulletinboard.sqlserver.SQLiteBulletinBoardServer;
public class BulletinBoardServerTest {
@Test
public void testAllServers() throws Exception {
GenericBulletinBoardServerTest bbst = new GenericBulletinBoardServerTest();
bbst.init(SQLiteBulletinBoardServer.class);
bbst.bulkTest();
bbst.close();
}
}

View File

@ -1,14 +1,14 @@
package meerkat.bulletinboard;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
@ -29,130 +29,239 @@ import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
public class GenericBulletinBoardServerTest {
private BulletinBoardServer bulletinBoardServer;
protected BulletinBoardServer bulletinBoardServer;
private ECDSASignature signers[];
private ByteString[] signerIDs;
private Random random;
private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
private static String KEYFILE_PASSWORD = "secret";
private static String KEYFILE_EXAMPLE3 = "/certs/enduser-certs/user3-key-with-password-shh.p12";
private static String KEYFILE_PASSWORD1 = "secret";
private static String KEYFILE_PASSWORD3 = "shh";
public static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt";
// private static String KEYFILE_EXAMPLE2 = "/certs/enduser-certs/user2-key.pem";
public void init(Class<?> cls) throws InstantiationException, IllegalAccessException, CertificateException, KeyStoreException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException, CommunicationException{
bulletinBoardServer = (BulletinBoardServer) cls.newInstance();
public static String CERT3_PEM_EXAMPLE = "/certs/enduser-certs/user3.crt";
bulletinBoardServer.init(File.createTempFile("meerkat-test", "db"));
private final int TAG_NUM = 5; // Number of tags.
private final int MESSAGE_NUM = 32; // Number of messages (2^TAG_NUM).
private String[] tags;
private byte[][] data;
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
/**
* @param bulletinBoardServer is an initialized server.
* @throws InstantiationException
* @throws IllegalAccessException
* @throws CertificateException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws IOException
* @throws UnrecoverableKeyException
* @throws CommunicationException
*/
public void init(BulletinBoardServer bulletinBoardServer) {
System.err.println("Starting to initialize GenericBulletinBoardServerTest");
long start = threadBean.getCurrentThreadCpuTime();
this.bulletinBoardServer = bulletinBoardServer;
signers = new ECDSASignature[2];
signerIDs = new ByteString[signers.length];
signers[0] = new ECDSASignature();
signers[1] = new ECDSASignature();
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
char[] password = KEYFILE_PASSWORD.toCharArray();
KeyStore.Builder keyStore = signers[0].getPKCS12KeyStoreBuilder(keyStream, password);
signers[0].loadSigningCertificate(keyStore);
signers[0].loadVerificationCertificates(getClass().getResourceAsStream(CERT1_PEM_EXAMPLE));
char[] password = KEYFILE_PASSWORD1.toCharArray();
KeyStore.Builder keyStoreBuilder = null;
try {
keyStoreBuilder = signers[0].getPKCS12KeyStoreBuilder(keyStream, password);
signers[0].loadSigningCertificate(keyStoreBuilder);
signers[0].loadVerificationCertificates(getClass().getResourceAsStream(CERT1_PEM_EXAMPLE));
keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE3);
password = KEYFILE_PASSWORD3.toCharArray();
keyStoreBuilder = signers[1].getPKCS12KeyStoreBuilder(keyStream, password);
signers[1].loadSigningCertificate(keyStoreBuilder);
signers[1].loadVerificationCertificates(getClass().getResourceAsStream(CERT3_PEM_EXAMPLE));
for (int i = 0 ; i < signers.length ; i++) {
signerIDs[i] = signers[i].getSignerID();
}
} catch (IOException e) {
System.err.println("Failed reading from signature file " + e.getMessage());
fail("Failed reading from signature file " + e.getMessage());
} catch (CertificateException e) {
System.err.println("Failed reading certificate " + e.getMessage());
fail("Failed reading certificate " + e.getMessage());
} catch (KeyStoreException e) {
System.err.println("Failed reading keystore " + e.getMessage());
fail("Failed reading keystore " + e.getMessage());
} catch (NoSuchAlgorithmException e) {
System.err.println("Couldn't find signing algorithm " + e.getMessage());
fail("Couldn't find signing algorithm " + e.getMessage());
} catch (UnrecoverableKeyException e) {
System.err.println("Couldn't find signing key " + e.getMessage());
fail("Couldn't find signing key " + e.getMessage());
}
random = new Random(0); // We use insecure randomness in tests for repeatability
long end = threadBean.getCurrentThreadCpuTime();
System.err.println("Finished initializing GenericBulletinBoardServerTest");
System.err.println("Time of operation: " + (end - start));
}
private byte randomByte(){
return (byte) random.nextInt();
}
private String randomString(){
return new BigInteger(130, random).toString(32);
}
public void bulkTest() throws CommunicationException, SignatureException, InvalidKeyException, CertificateException, IOException{
final int TAG_NUM = 5; // Number of tags.
final int MESSAGE_NUM = 32; // Number of messages (2^TAG_NUM).
/**
* Tests writing of several messages with multiple tags and signatures.
* @throws CommunicationException
* @throws SignatureException
* @throws InvalidKeyException
* @throws CertificateException
* @throws IOException
*/
public void testInsert() {
System.err.println("Starting to insert messages to DB");
long start = threadBean.getCurrentThreadCpuTime();
final int BYTES_PER_MESSAGE_DATA = 50; // Message size.
String[] tags = new String[TAG_NUM];
byte[][] data = new byte[MESSAGE_NUM][BYTES_PER_MESSAGE_DATA];
tags = new String[TAG_NUM];
data = new byte[MESSAGE_NUM][BYTES_PER_MESSAGE_DATA];
UnsignedBulletinBoardMessage.Builder unsignedMsgBuilder;
BulletinBoardMessage.Builder msgBuilder;
int i,j;
int i, j;
// Generate random data.
for (i = 1 ; i <= MESSAGE_NUM ; i++){
for (j = 0 ; j < BYTES_PER_MESSAGE_DATA ; j++){
data[i-1][j] = randomByte();
for (i = 1; i <= MESSAGE_NUM; i++) {
for (j = 0; j < BYTES_PER_MESSAGE_DATA; j++) {
data[i - 1][j] = randomByte();
}
}
for (i = 0 ; i < TAG_NUM ; i++){
for (i = 0; i < TAG_NUM; i++) {
tags[i] = randomString();
}
// Build messages.
for (i = 1 ; i <= MESSAGE_NUM ; i++){
for (i = 1; i <= MESSAGE_NUM; i++) {
unsignedMsgBuilder = UnsignedBulletinBoardMessage.newBuilder()
.setData(ByteString.copyFrom(data[i-1]));
.setData(ByteString.copyFrom(data[i - 1]));
// Add tags based on bit-representation of message number.
int copyI = i;
for (j = 0 ; j < TAG_NUM ; j++){
if (copyI % 2 == 1){
for (j = 0; j < TAG_NUM; j++) {
if (copyI % 2 == 1) {
unsignedMsgBuilder.addTag(tags[j]);
}
copyI >>>= 1;
}
// Build message.
msgBuilder = BulletinBoardMessage.newBuilder()
.setMsg(unsignedMsgBuilder.build());
// Add signatures.
if (i % 2 == 1){
signers[0].updateContent(msgBuilder.getMsg());
msgBuilder.addSig(signers[0].sign());
}
// Post message.
bulletinBoardServer.postMessage(msgBuilder.build());
try {
if (i % 2 == 1) {
signers[0].updateContent(msgBuilder.getMsg());
msgBuilder.addSig(signers[0].sign());
if (i % 4 == 1) {
signers[1].updateContent(msgBuilder.getMsg());
msgBuilder.addSig(signers[1].sign());
}
}
} catch (SignatureException e) {
fail(e.getMessage());
}
// Post message.
try {
bulletinBoardServer.postMessage(msgBuilder.build());
} catch (CommunicationException e) {
fail(e.getMessage());
}
}
long end = threadBean.getCurrentThreadCpuTime();
System.err.println("Finished inserting messages to DB");
System.err.println("Time of operation: " + (end - start));
}
/**
* Tests retrieval of messages written in {@Link #testInsert()}
* Only queries using one tag filter
*/
public void testSimpleTagAndSignature(){
System.err.println("Starting to test tag and signature mechanism");
long start = threadBean.getCurrentThreadCpuTime();
List<BulletinBoardMessage> messages;
// Check tag mechanism
for (i = 0 ; i < TAG_NUM ; i++){
for (int i = 0 ; i < TAG_NUM ; i++){
// Retrieve messages having tag i
List<BulletinBoardMessage> messages =
bulletinBoardServer.readMessages(
MessageFilterList.newBuilder()
.addFilter(MessageFilter.newBuilder()
.setType(FilterType.TAG)
.setTag(tags[i])
try {
messages = bulletinBoardServer.readMessages(
MessageFilterList.newBuilder()
.addFilter(MessageFilter.newBuilder()
.setType(FilterType.TAG)
.setTag(tags[i])
.build()
)
.build()
)
.build()
)
.getMessageList();
} catch (CommunicationException e) {
fail(e.getMessage());
return;
}
// Assert that the number of retrieved messages is correct.
assertThat(messages.size(), is(MESSAGE_NUM / 2));
// Assert the identity of the messages.
for (BulletinBoardMessage msg : messages){
// Assert serial number and raw data.
@ -161,20 +270,122 @@ public class GenericBulletinBoardServerTest {
assertThat(msg.getMsg().getData().toByteArray(), is(data[(int) msg.getEntryNum() - 1]));
// Assert signatures.
if (msg.getEntryNum() % 2 == 1){
signers[0].initVerify(msg.getSig(0));
signers[0].updateContent(msg.getMsg());
assertTrue("Signature did not verify!", signers[0].verify());
try {
if (msg.getEntryNum() % 2 == 1) {
signers[0].initVerify(msg.getSig(0));
signers[0].updateContent(msg.getMsg());
assertTrue("Signature did not verify!", signers[0].verify());
if (msg.getEntryNum() % 4 == 1) {
signers[1].initVerify(msg.getSig(1));
signers[1].updateContent(msg.getMsg());
assertTrue("Signature did not verify!", signers[1].verify());
assertThat(msg.getSigCount(), is(2));
} else {
assertThat(msg.getSigCount(), is(1));
}
} else {
assertThat(msg.getSigCount(), is(0));
}
} catch (Exception e) {
fail(e.getMessage());
}
}
}
long end = threadBean.getCurrentThreadCpuTime();
System.err.println("Finished testing tag and signature mechanism");
System.err.println("Time of operation: " + (end - start));
}
/**
* Tests retrieval of messages written in {@Link #testInsert()} using multiple tags/signature filters.
*/
public void testEnhancedTagsAndSignatures(){
System.err.println("Starting to test multiple tags and signatures");
long start = threadBean.getCurrentThreadCpuTime();
List<BulletinBoardMessage> messages;
MessageFilterList.Builder filterListBuilder = MessageFilterList.newBuilder();
int expectedMsgCount = MESSAGE_NUM;
// Check multiple tag filters.
for (int i = 0 ; i < TAG_NUM ; i++) {
filterListBuilder.addFilter(
MessageFilter.newBuilder()
.setType(FilterType.TAG)
.setTag(tags[i])
.build()
);
try {
messages = bulletinBoardServer.readMessages(filterListBuilder.build()).getMessageList();
} catch (CommunicationException e) {
System.err.println("Failed retrieving multi-tag messages from DB: " + e.getMessage());
fail("Failed retrieving multi-tag messages from DB: " + e.getMessage());
return;
}
expectedMsgCount /= 2;
assertThat(messages.size(), is(expectedMsgCount));
for (BulletinBoardMessage msg : messages) {
for (int j = 0 ; j <= i ; j++) {
assertThat((msg.getEntryNum() >>> j) % 2, is((long) 1));
}
}
}
// Check multiple signature filters.
filterListBuilder = MessageFilterList.newBuilder()
.addFilter(MessageFilter.newBuilder()
.setType(FilterType.SIGNER_ID)
.setId(signerIDs[0])
.build())
.addFilter(MessageFilter.newBuilder()
.setType(FilterType.SIGNER_ID)
.setId(signerIDs[1])
.build());
try {
messages = bulletinBoardServer.readMessages(filterListBuilder.build()).getMessageList();
} catch (CommunicationException e) {
System.err.println("Failed retrieving multi-signature message from DB: " + e.getMessage());
fail("Failed retrieving multi-signature message from DB: " + e.getMessage());
return;
}
assertThat(messages.size(), is(MESSAGE_NUM / 4));
for (BulletinBoardMessage message : messages) {
assertThat(message.getEntryNum() % 4, is((long) 1));
}
long end = threadBean.getCurrentThreadCpuTime();
System.err.println("Finished testing multiple tags and signatures");
System.err.println("Time of operation: " + (end - start));
}
public void close(){
signers[0].clearSigningKey();
signers[1].clearSigningKey();
try {
bulletinBoardServer.close();
} catch (CommunicationException e) {
System.err.println("Error closing server " + e.getMessage());
fail("Error closing server " + e.getMessage());
}
}
}

View File

@ -0,0 +1,122 @@
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));
}
}

View File

@ -0,0 +1,126 @@
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));
}
}

View File

@ -0,0 +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));
}
}

Binary file not shown.

View File

@ -1,6 +1,7 @@
#Sun Mar 20 15:13:00 IST 2016
#Tue Aug 05 03:26:05 IDT 2014
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.9-all.zip
distributionSha256Sum=4647967f8de78d6d6d8093cdac50f368f8c2b8038f41a5afe1c3bce4c69219a9

10
gradlew vendored
View File

@ -42,6 +42,11 @@ case "`uname`" in
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@ -56,9 +61,9 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@ -109,7 +114,6 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`

View File

@ -45,10 +45,12 @@ dependencies {
// Google protobufs
compile 'com.google.protobuf:protobuf-java:3.+'
// ListeningExecutor
compile 'com.google.guava:guava:11.0.+'
// Crypto
compile 'org.factcenter.qilin:qilin:1.1+'
compile 'org.factcenter.qilin:qilin:1.2+'
compile 'org.bouncycastle:bcprov-jdk15on:1.53'
compile 'org.bouncycastle:bcpkix-jdk15on:1.53'
testCompile 'junit:junit:4.+'

View File

@ -1,48 +0,0 @@
package meerkat.bulletinboard;
import meerkat.comm.*;
import static meerkat.protobuf.BulletinBoardAPI.*;
import java.util.List;
/**
* Created by talm on 24/10/15.
*/
public interface BulletinBoard {
/**
* Post a message to the bulletin board
* @param msg
*/
MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException;
/**
* Check how "safe" a given message is
* @param id
* @return a normalized "redundancy score" from 0 (local only) to 1 (fully published)
*/
float getRedundancy(MessageID id);
/**
* Read all messages posted matching the given filter
* Note that if messages haven't been "fully posted", this might return a different
* set of messages in different calls. However, messages that are fully posted
* are guaranteed to be included.
* @param filter return only messages that match the filter (null means no filtering).
* @param max maximum number of messages to return (0=no limit)
* @return
*/
List<BulletinBoardMessage> readMessages(MessageFilter filter, int max);
interface MessageCallback {
void handleNewMessage(BulletinBoardMessage msg);
}
/**
* Register a callback that will be called with each new message that is posted.
* The callback will be called only once for each message.
* @param callback
* @param filter only call back for messages that match the filter.
*/
void registerNewMessageCallback(MessageCallback callback, MessageFilter filter);
}

View File

@ -0,0 +1,54 @@
package meerkat.bulletinboard;
import meerkat.comm.*;
import meerkat.protobuf.Voting.*;
import static meerkat.protobuf.BulletinBoardAPI.*;
import java.util.List;
/**
* Created by talm on 24/10/15.
*/
public interface BulletinBoardClient {
interface ClientCallback<T> {
void handleCallback(T msg);
void handleFailure(Throwable t);
}
/**
* Initialize the client to use some specified servers
* @param clientParams contains the parameters required for the client setup
*/
void init(BulletinBoardClientParams clientParams);
/**
* Post a message to the bulletin board
* @param msg
*/
MessageID postMessage(BulletinBoardMessage msg, ClientCallback<?> callback);
/**
* Check how "safe" a given message is
* @param id
* @return a normalized "redundancy score" from 0 (local only) to 1 (fully published)
*/
void getRedundancy(MessageID id, ClientCallback<Float> callback);
/**
* Read all messages posted matching the given filter
* Note that if messages haven't been "fully posted", this might return a different
* set of messages in different calls. However, messages that are fully posted
* are guaranteed to be included.
* @param filterList return only messages that match the filters (null means no filtering).
*/
void readMessages(MessageFilterList filterList, ClientCallback<List<BulletinBoardMessage>> callback);
/**
* Closes all connections, if any.
* This is done in a synchronous (blocking) way.
*/
void close();
}

View File

@ -3,8 +3,6 @@ package meerkat.bulletinboard;
import meerkat.comm.CommunicationException;
import meerkat.protobuf.BulletinBoardAPI.*;
import java.io.File;
/**
* Created by Arbel on 07/11/15.
*
@ -19,7 +17,7 @@ public interface BulletinBoardServer{
* It also establishes the connection to the DB.
* @throws CommunicationException on DB connection error.
*/
public void init(File meerkatDB) throws CommunicationException;
public void init(String meerkatDB) throws CommunicationException;
/**
* Post a message to bulletin board.

View File

@ -1,9 +1,8 @@
package meerkat.crypto;
import com.google.protobuf.ByteString;
import com.google.protobuf.Message;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.PasswordCallback;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
@ -11,7 +10,6 @@ import java.security.KeyStore;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.List;
import static meerkat.protobuf.Crypto.*;
/**
@ -85,6 +83,10 @@ public interface DigitalSignature {
throws IOException, CertificateException, UnrecoverableKeyException;
/**
* @return the signer ID if it exists; null otherwise.
*/
public ByteString getSignerID();
/**
* Clear the signing key (will require authentication to use again).

View File

@ -2,43 +2,25 @@ package meerkat.crypto.concrete;
import com.google.protobuf.ByteString;
import com.google.protobuf.Message;
import meerkat.crypto.DigitalSignature;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Crypto.Signature;
import meerkat.util.Hex;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.signers.DSAKCalculator;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
import org.bouncycastle.jcajce.provider.asymmetric.util.DSAEncoder;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.ECKeyUtil;
import org.bouncycastle.jce.ECPointUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.*;
import java.security.interfaces.ECPrivateKey;
import java.security.spec.ECParameterSpec;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**

View File

@ -108,7 +108,6 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur
logger.error("Certificate must be in X509 format; got {} instead!", cert.getClass().getCanonicalName());
continue;
}
PublicKey pubKey = cert.getPublicKey();
ByteString keyId = computeCertificateFingerprint(cert);
loadedCertificates.put(keyId, cert);
}
@ -301,6 +300,11 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur
throw new UnrecoverableKeyException("Didn't find valid private key entry in keystore!");
}
@Override
public ByteString getSignerID() {
return loadedSigningKeyId;
}
public void clearSigningKey() {
try {
// TODO: Check if this really clears the key from memory

View File

@ -1,7 +1,6 @@
package meerkat.crypto.concrete;
import com.google.protobuf.ByteString;
import com.google.protobuf.CodedOutputStream;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import meerkat.crypto.Encryption;
@ -14,22 +13,18 @@ import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.BigIntegers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qilin.primitives.concrete.ECElGamal;
import qilin.primitives.concrete.ECGroup;
import qilin.primitives.PseudorandomGenerator;
import qilin.primitives.generic.ElGamal;
import qilin.util.PRGRandom;
import qilin.util.Pair;
import org.factcenter.qilin.primitives.concrete.ECElGamal;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import org.factcenter.qilin.util.PRGRandom;
import org.factcenter.qilin.util.Pair;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.security.spec.*;
import java.util.Random;

View File

@ -0,0 +1,49 @@
package meerkat.util;
import meerkat.protobuf.BulletinBoardAPI;
import meerkat.protobuf.BulletinBoardAPI.*;
import meerkat.protobuf.Crypto.*;
import java.util.Comparator;
import java.util.List;
/**
* Created by Arbel Deutsch Peled on 05-Dec-15.
* This class implements a comparison between BulletinBoardMessage instances that disregards:
* 1. The entry number (since this can be different between database instances)
* 2. The order of the signatures
*/
public class BulletinBoardMessageComparator implements Comparator<BulletinBoardMessage> {
/**
* Compare the messages
* @param msg1
* @param msg2
* @return 0 if the messages are equivalent (see above) and -1 otherwise.
*/
@Override
public int compare(BulletinBoardMessage msg1, BulletinBoardMessage msg2) {
List<Signature> msg1Sigs = msg1.getSigList();
List<Signature> msg2Sigs = msg2.getSigList();
// Compare unsigned message
if (!msg1.getMsg().equals(msg2.getMsg())){
return -1;
}
// Compare signatures
if (msg1Sigs.size() != msg2Sigs.size()){
return -1;
}
for (Signature sig : msg1Sigs){
if (!msg2Sigs.contains(sig)) {
return -1;
}
}
return 0;
}
}

View File

@ -2,14 +2,12 @@ package meerkat.crypto.concrete;
import com.google.protobuf.ByteString;
import com.google.protobuf.Message;
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
import meerkat.crypto.concrete.ECDSASignature;
import meerkat.protobuf.Crypto;
import org.junit.Test;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.KeyStore;
import static org.junit.Assert.*;
/**

View File

@ -3,7 +3,7 @@ package meerkat.crypto.concrete;
import com.google.protobuf.ByteString;
import com.google.protobuf.Message;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.BulletinBoardAPI.*;
import meerkat.crypto.concrete.ECDSASignature;
import org.junit.Before;
import org.junit.Test;

View File

@ -1,19 +1,16 @@
package meerkat.crypto.concrete;
import meerkat.protobuf.BulletinBoardAPI;
import meerkat.protobuf.ConcreteCrypto;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Voting;
import org.bouncycastle.math.ec.ECPoint;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qilin.primitives.concrete.ECElGamal;
import qilin.primitives.concrete.ECGroup;
import qilin.primitives.generic.ElGamal;
import qilin.util.Pair;
import org.factcenter.qilin.primitives.concrete.ECElGamal;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import org.factcenter.qilin.util.Pair;
import java.math.BigInteger;
import java.util.Random;

View File

@ -11,10 +11,10 @@ import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qilin.primitives.concrete.ECElGamal;
import qilin.primitives.concrete.ECGroup;
import qilin.primitives.generic.ElGamal;
import qilin.util.Pair;
import org.factcenter.qilin.primitives.concrete.ECElGamal;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import org.factcenter.qilin.primitives.generic.ElGamal;
import org.factcenter.qilin.util.Pair;
import java.io.ByteArrayInputStream;
import java.security.KeyFactory;
@ -77,7 +77,7 @@ public class ECElGamalUtils {
try {
java.lang.reflect.Method newBuilder = plaintextMessageType.getMethod("newBuilder");
GeneratedMessage.Builder builder = (GeneratedMessage.Builder) newBuilder.invoke(plaintextMessageType);
GeneratedMessage.Builder<?> builder = (GeneratedMessage.Builder<?>) newBuilder.invoke(plaintextMessageType);
builder.mergeDelimitedFrom(in);
return plaintextMessageType.cast(builder.build());
} catch (Exception e) {

View File

@ -5,4 +5,8 @@ package meerkat.rest;
*/
public interface Constants {
public static final String MEDIATYPE_PROTOBUF = "application/x-protobuf";
public static final String BULLETIN_BOARD_SERVER_PATH = "/bbserver";
public static final String READ_MESSAGES_PATH = "/readmessages";
public static final String POST_MESSAGE_PATH = "/postmessage";
}

View File

@ -32,7 +32,7 @@ public class ProtobufMessageBodyReader implements MessageBodyReader<Message> {
InputStream entityStream) throws IOException, WebApplicationException {
try {
Method newBuilder = type.getMethod("newBuilder");
GeneratedMessage.Builder builder = (GeneratedMessage.Builder) newBuilder.invoke(type);
GeneratedMessage.Builder<?> builder = (GeneratedMessage.Builder<?>) newBuilder.invoke(type);
return builder.mergeFrom(entityStream).build();
} catch (Exception e) {
throw new WebApplicationException(e);

View File

@ -3,4 +3,5 @@ include 'voting-booth'
include 'bulletin-board-server'
include 'polling-station'
include 'restful-api-common'
include 'mixer'
include 'mixer'
include 'bulletin-board-client'