Merge branch 'Bulletin-Board-Batch'
						commit
						50bcca8da3
					
				| 
						 | 
					@ -381,11 +381,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
 | 
				
			||||||
        @Override
 | 
					        @Override
 | 
				
			||||||
        public void onSuccess(Boolean msg) {
 | 
					        public void onSuccess(Boolean msg) {
 | 
				
			||||||
            closeBatch(
 | 
					            closeBatch(
 | 
				
			||||||
                    CloseBatchMessage.newBuilder()
 | 
					                    completeBatch.getCloseBatchMessage(),
 | 
				
			||||||
                            .setBatchId(completeBatch.getBeginBatchMessage().getBatchId())
 | 
					 | 
				
			||||||
                            .setSig(completeBatch.getSignature())
 | 
					 | 
				
			||||||
                            .setBatchLength(completeBatch.getBatchDataList().size())
 | 
					 | 
				
			||||||
                            .build(),
 | 
					 | 
				
			||||||
                    callback
 | 
					                    callback
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ package meerkat.bulletinboard.workers.singleserver;
 | 
				
			||||||
import meerkat.bulletinboard.CompleteBatch;
 | 
					import meerkat.bulletinboard.CompleteBatch;
 | 
				
			||||||
import meerkat.bulletinboard.SingleServerWorker;
 | 
					import meerkat.bulletinboard.SingleServerWorker;
 | 
				
			||||||
import meerkat.comm.CommunicationException;
 | 
					import meerkat.comm.CommunicationException;
 | 
				
			||||||
 | 
					import meerkat.comm.MessageInputStream;
 | 
				
			||||||
import meerkat.protobuf.BulletinBoardAPI.*;
 | 
					import meerkat.protobuf.BulletinBoardAPI.*;
 | 
				
			||||||
import meerkat.rest.Constants;
 | 
					import meerkat.rest.Constants;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +13,9 @@ import javax.ws.rs.client.Entity;
 | 
				
			||||||
import javax.ws.rs.client.WebTarget;
 | 
					import javax.ws.rs.client.WebTarget;
 | 
				
			||||||
import javax.ws.rs.core.GenericType;
 | 
					import javax.ws.rs.core.GenericType;
 | 
				
			||||||
import javax.ws.rs.core.Response;
 | 
					import javax.ws.rs.core.Response;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.InputStream;
 | 
				
			||||||
 | 
					import java.lang.reflect.InvocationTargetException;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH;
 | 
					import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH;
 | 
				
			||||||
| 
						 | 
					@ -40,29 +44,33 @@ public class SingleServerReadBatchWorker extends SingleServerWorker<BatchSpecifi
 | 
				
			||||||
        Client client = clientLocal.get();
 | 
					        Client client = clientLocal.get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        WebTarget webTarget;
 | 
					        WebTarget webTarget;
 | 
				
			||||||
        Response response;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Get the batch data
 | 
					        // Get the batch data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(READ_BATCH_PATH);
 | 
					        webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(READ_BATCH_PATH);
 | 
				
			||||||
        response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(
 | 
					        InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF), InputStream.class);
 | 
				
			||||||
                Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Retrieve answer
 | 
					        MessageInputStream<BatchData> inputStream = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // If a BatchDataList is returned: the read was successful
 | 
					            inputStream = MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BatchData.class);
 | 
				
			||||||
            return response.readEntity(BatchDataList.class).getDataList();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        } catch (ProcessingException | IllegalStateException e) {
 | 
					            return inputStream.asList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (IOException | InvocationTargetException e) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Read failed
 | 
					            // Read failed
 | 
				
			||||||
            throw new CommunicationException("Could not contact the server");
 | 
					            throw new CommunicationException("Could not contact the server or server returned illegal result");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        } catch (NoSuchMethodException | IllegalAccessException e) {
 | 
				
			||||||
        finally {
 | 
					
 | 
				
			||||||
            response.close();
 | 
					            throw new CommunicationException("MessageInputStream error");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                inputStream.close();
 | 
				
			||||||
 | 
					            } catch (IOException ignored) {}
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,8 @@ package meerkat.bulletinboard.workers.singleserver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import meerkat.bulletinboard.SingleServerWorker;
 | 
					import meerkat.bulletinboard.SingleServerWorker;
 | 
				
			||||||
import meerkat.comm.CommunicationException;
 | 
					import meerkat.comm.CommunicationException;
 | 
				
			||||||
 | 
					import meerkat.comm.MessageInputStream;
 | 
				
			||||||
 | 
					import meerkat.protobuf.BulletinBoardAPI;
 | 
				
			||||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessageList;
 | 
					import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessageList;
 | 
				
			||||||
import meerkat.protobuf.BulletinBoardAPI.MessageFilterList;
 | 
					import meerkat.protobuf.BulletinBoardAPI.MessageFilterList;
 | 
				
			||||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
 | 
					import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
 | 
				
			||||||
| 
						 | 
					@ -13,6 +15,9 @@ import javax.ws.rs.client.Entity;
 | 
				
			||||||
import javax.ws.rs.client.WebTarget;
 | 
					import javax.ws.rs.client.WebTarget;
 | 
				
			||||||
import javax.ws.rs.core.Response;
 | 
					import javax.ws.rs.core.Response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.InputStream;
 | 
				
			||||||
 | 
					import java.lang.reflect.InvocationTargetException;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH;
 | 
					import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH;
 | 
				
			||||||
| 
						 | 
					@ -38,30 +43,33 @@ public class SingleServerReadMessagesWorker extends SingleServerWorker<MessageFi
 | 
				
			||||||
        Client client = clientLocal.get();
 | 
					        Client client = clientLocal.get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        WebTarget webTarget;
 | 
					        WebTarget webTarget;
 | 
				
			||||||
        Response response;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Send request to Server
 | 
					        // Send request to Server
 | 
				
			||||||
        webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH);
 | 
					        webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH);
 | 
				
			||||||
        response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(
 | 
					        InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF), InputStream.class);
 | 
				
			||||||
                Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Retrieve answer
 | 
					        MessageInputStream<BulletinBoardMessage> inputStream = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // If a BulletinBoardMessageList is returned: the read was successful
 | 
					            inputStream = MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BulletinBoardMessage.class);
 | 
				
			||||||
            return response.readEntity(BulletinBoardMessageList.class).getMessageList();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        } catch (ProcessingException | IllegalStateException e) {
 | 
					            return inputStream.asList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (IOException | InvocationTargetException e) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Read failed
 | 
					            // Read failed
 | 
				
			||||||
            throw new CommunicationException("Could not contact the server");
 | 
					            throw new CommunicationException("Could not contact the server or server returned illegal result");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        } catch (NoSuchMethodException | IllegalAccessException e) {
 | 
				
			||||||
        finally {
 | 
					 | 
				
			||||||
            response.close();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            throw new CommunicationException("MessageInputStream error");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                inputStream.close();
 | 
				
			||||||
 | 
					            } catch (IOException ignored) {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
import com.google.common.util.concurrent.FutureCallback;
 | 
					import com.google.common.util.concurrent.FutureCallback;
 | 
				
			||||||
import com.google.protobuf.ByteString;
 | 
					import com.google.protobuf.*;
 | 
				
			||||||
 | 
					import com.google.protobuf.Timestamp;
 | 
				
			||||||
import meerkat.bulletinboard.AsyncBulletinBoardClient;
 | 
					import meerkat.bulletinboard.AsyncBulletinBoardClient;
 | 
				
			||||||
import meerkat.bulletinboard.CompleteBatch;
 | 
					import meerkat.bulletinboard.CompleteBatch;
 | 
				
			||||||
import meerkat.bulletinboard.GenericBatchDigitalSignature;
 | 
					import meerkat.bulletinboard.GenericBatchDigitalSignature;
 | 
				
			||||||
| 
						 | 
					@ -284,6 +285,11 @@ public class ThreadedBulletinBoardClientIntegrationTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        completeBatch.setTimestamp(Timestamp.newBuilder()
 | 
				
			||||||
 | 
					                .setSeconds(Math.abs(90))
 | 
				
			||||||
 | 
					                .setNanos(50)
 | 
				
			||||||
 | 
					                .build());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        signers[signer].updateContent(completeBatch);
 | 
					        signers[signer].updateContent(completeBatch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        completeBatch.setSignature(signers[signer].sign());
 | 
					        completeBatch.setSignature(signers[signer].sign());
 | 
				
			||||||
| 
						 | 
					@ -357,6 +363,10 @@ public class ThreadedBulletinBoardClientIntegrationTest {
 | 
				
			||||||
                        .addTag("Signature")
 | 
					                        .addTag("Signature")
 | 
				
			||||||
                        .addTag("Trustee")
 | 
					                        .addTag("Trustee")
 | 
				
			||||||
                        .setData(ByteString.copyFrom(b1))
 | 
					                        .setData(ByteString.copyFrom(b1))
 | 
				
			||||||
 | 
					                        .setTimestamp(Timestamp.newBuilder()
 | 
				
			||||||
 | 
					                                .setSeconds(20)
 | 
				
			||||||
 | 
					                                .setNanos(30)
 | 
				
			||||||
 | 
					                                .build())
 | 
				
			||||||
                        .build())
 | 
					                        .build())
 | 
				
			||||||
                .addSig(Crypto.Signature.newBuilder()
 | 
					                .addSig(Crypto.Signature.newBuilder()
 | 
				
			||||||
                        .setType(Crypto.SignatureType.DSA)
 | 
					                        .setType(Crypto.SignatureType.DSA)
 | 
				
			||||||
| 
						 | 
					@ -440,6 +450,10 @@ public class ThreadedBulletinBoardClientIntegrationTest {
 | 
				
			||||||
        CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder()
 | 
					        CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder()
 | 
				
			||||||
                .setBatchId(BATCH_ID)
 | 
					                .setBatchId(BATCH_ID)
 | 
				
			||||||
                .setBatchLength(BATCH_LENGTH)
 | 
					                .setBatchLength(BATCH_LENGTH)
 | 
				
			||||||
 | 
					                .setTimestamp(Timestamp.newBuilder()
 | 
				
			||||||
 | 
					                        .setSeconds(50)
 | 
				
			||||||
 | 
					                        .setNanos(80)
 | 
				
			||||||
 | 
					                        .build())
 | 
				
			||||||
                .setSig(completeBatch.getSignature())
 | 
					                .setSig(completeBatch.getSignature())
 | 
				
			||||||
                .build();
 | 
					                .build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -525,6 +539,10 @@ public class ThreadedBulletinBoardClientIntegrationTest {
 | 
				
			||||||
                        .setBatchId(NON_EXISTENT_BATCH_ID)
 | 
					                        .setBatchId(NON_EXISTENT_BATCH_ID)
 | 
				
			||||||
                        .setBatchLength(1)
 | 
					                        .setBatchLength(1)
 | 
				
			||||||
                        .setSig(Crypto.Signature.getDefaultInstance())
 | 
					                        .setSig(Crypto.Signature.getDefaultInstance())
 | 
				
			||||||
 | 
					                        .setTimestamp(Timestamp.newBuilder()
 | 
				
			||||||
 | 
					                                .setSeconds(9)
 | 
				
			||||||
 | 
					                                .setNanos(12)
 | 
				
			||||||
 | 
					                                .build())
 | 
				
			||||||
                        .build();
 | 
					                        .build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Try to close the (unopened) batch;
 | 
					        // Try to close the (unopened) batch;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -446,9 +446,10 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		sql = sqlQueryProvider.getSQLString(QueryType.FIND_MSG_ID);
 | 
							sql = sqlQueryProvider.getSQLString(QueryType.FIND_MSG_ID);
 | 
				
			||||||
		Map namedParameters = new HashMap();
 | 
							Map namedParameters = new HashMap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		namedParameters.put(QueryType.FIND_MSG_ID.getParamName(0),msgID);
 | 
							namedParameters.put(QueryType.FIND_MSG_ID.getParamName(0),msgID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		List<Long> entryNums = jdbcTemplate.query(sql, new MapSqlParameterSource(namedParameters), new LongMapper());
 | 
							List<Long> entryNums = jdbcTemplate.query(sql, namedParameters, new LongMapper());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (entryNums.size() > 0){
 | 
							if (entryNums.size() > 0){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -462,7 +463,7 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
 | 
				
			||||||
			namedParameters.put(QueryType.INSERT_MSG.getParamName(2), msg.getMsg().toByteArray());
 | 
								namedParameters.put(QueryType.INSERT_MSG.getParamName(2), msg.getMsg().toByteArray());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			KeyHolder keyHolder = new GeneratedKeyHolder();
 | 
								KeyHolder keyHolder = new GeneratedKeyHolder();
 | 
				
			||||||
			jdbcTemplate.update(sql,new MapSqlParameterSource(namedParameters),keyHolder);
 | 
								jdbcTemplate.update(sql, new MapSqlParameterSource(namedParameters), keyHolder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			entryNum = keyHolder.getKey().longValue();
 | 
								entryNum = keyHolder.getKey().longValue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -785,6 +786,9 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
 | 
				
			||||||
						.build()
 | 
											.build()
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Add timestamp to CompleteBatch
 | 
				
			||||||
 | 
							completeBatch.setTimestamp(message.getTimestamp());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Add actual batch data to CompleteBatch
 | 
							// Add actual batch data to CompleteBatch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA);
 | 
							sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA);
 | 
				
			||||||
| 
						 | 
					@ -903,20 +907,20 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		long lastEntryNum = getLastMessageEntry();
 | 
							long lastEntryNum = getLastMessageEntry();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		long checksum = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		Iterator<SingleSyncQuery> queryIterator = syncQuery.getQueryList().iterator();
 | 
							Iterator<SingleSyncQuery> queryIterator = syncQuery.getQueryList().iterator();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		SingleSyncQuery currentQuery = queryIterator.next();
 | 
							SingleSyncQuery currentQuery = queryIterator.next();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		List<BulletinBoardMessage> messageStubs = readMessageStubs(syncQuery.getFilterList());
 | 
							List<BulletinBoardMessage> messageStubs = readMessageStubs(syncQuery.getFilterList());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Checksum checksum = new SimpleChecksum();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (BulletinBoardMessage message : messageStubs){
 | 
							for (BulletinBoardMessage message : messageStubs){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Check for end of current query
 | 
								// Check for end of current query
 | 
				
			||||||
			if (timestampComparator.compare(message.getMsg().getTimestamp(), currentQuery.getTimeOfSync()) > 0){
 | 
								if (timestampComparator.compare(message.getMsg().getTimestamp(), currentQuery.getTimeOfSync()) > 0){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (checksum == currentQuery.getChecksum()){
 | 
									if (checksum.getChecksum() == currentQuery.getChecksum()){
 | 
				
			||||||
					lastTimeOfSync = currentQuery.getTimeOfSync();
 | 
										lastTimeOfSync = currentQuery.getTimeOfSync();
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					break;
 | 
										break;
 | 
				
			||||||
| 
						 | 
					@ -932,13 +936,13 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Advance checksum
 | 
								// Advance checksum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ByteString messageID = message.getMsg().getData();
 | 
								ByteString messageID = message.getMsg().getData();	// The data field contains the message ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			checksum &= messageID.byteAt(0) & messageID.byteAt(1) & messageID.byteAt(2) & messageID.byteAt(3);
 | 
								checksum.update(messageID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (checksum == currentQuery.getChecksum()){
 | 
							if (checksum.getChecksum() == currentQuery.getChecksum()){
 | 
				
			||||||
			lastTimeOfSync = currentQuery.getTimeOfSync();
 | 
								lastTimeOfSync = currentQuery.getTimeOfSync();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ import meerkat.protobuf.BulletinBoardAPI.*;
 | 
				
			||||||
import org.sqlite.SQLiteDataSource;
 | 
					import org.sqlite.SQLiteDataSource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.sql.DataSource;
 | 
					import javax.sql.DataSource;
 | 
				
			||||||
 | 
					import java.text.MessageFormat;
 | 
				
			||||||
import java.util.LinkedList;
 | 
					import java.util.LinkedList;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,19 +26,97 @@ public class SQLiteQueryProvider implements BulletinBoardSQLServer.SQLQueryProvi
 | 
				
			||||||
        switch(queryType) {
 | 
					        switch(queryType) {
 | 
				
			||||||
            case ADD_SIGNATURE:
 | 
					            case ADD_SIGNATURE:
 | 
				
			||||||
                return "INSERT OR IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (:EntryNum,:SignerId,:Signature)";
 | 
					                return "INSERT OR IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (:EntryNum,:SignerId,:Signature)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case CONNECT_TAG:
 | 
					            case CONNECT_TAG:
 | 
				
			||||||
                return "INSERT OR IGNORE INTO MsgTagTable (TagId, EntryNum)"
 | 
					                return "INSERT OR IGNORE INTO MsgTagTable (TagId, EntryNum)"
 | 
				
			||||||
                    + " SELECT TagTable.TagId, :EntryNum AS EntryNum FROM TagTable WHERE Tag = :Tag";
 | 
					                    + " SELECT TagTable.TagId, :EntryNum AS EntryNum FROM TagTable WHERE Tag = :Tag";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case FIND_MSG_ID:
 | 
					            case FIND_MSG_ID:
 | 
				
			||||||
                return "SELECT EntryNum From MsgTable WHERE MsgId = :MsgId";
 | 
					                return "SELECT EntryNum From MsgTable WHERE MsgId = :MsgId";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case FIND_TAG_ID:
 | 
				
			||||||
 | 
					                return MessageFormat.format(
 | 
				
			||||||
 | 
					                        "SELECT TagId FROM TagTable WHERE Tag = :{0}",
 | 
				
			||||||
 | 
					                        QueryType.FIND_TAG_ID.getParamName(0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case GET_MESSAGES:
 | 
					            case GET_MESSAGES:
 | 
				
			||||||
                return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
 | 
					                return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case COUNT_MESSAGES:
 | 
				
			||||||
 | 
					                return "SELECT COUNT(MsgTable.EntryNum) FROM MsgTable";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case GET_MESSAGE_STUBS:
 | 
				
			||||||
 | 
					                return "SELECT MsgTable.EntryNum, MsgTable.MsgId, MsgTable.ExactTime FROM MsgTable";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case GET_SIGNATURES:
 | 
					            case GET_SIGNATURES:
 | 
				
			||||||
                return "SELECT Signature FROM SignatureTable WHERE EntryNum = :EntryNum";
 | 
					                return "SELECT Signature FROM SignatureTable WHERE EntryNum = :EntryNum";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case INSERT_MSG:
 | 
					            case INSERT_MSG:
 | 
				
			||||||
                return "INSERT INTO MsgTable (MsgId, Msg) VALUES(:MsgId,:Msg)";
 | 
					                return "INSERT INTO MsgTable (MsgId, Msg) VALUES(:MsgId,:Msg)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case INSERT_NEW_TAG:
 | 
					            case INSERT_NEW_TAG:
 | 
				
			||||||
                return "INSERT OR IGNORE INTO TagTable(Tag) VALUES (:Tag)";
 | 
					                return "INSERT OR IGNORE INTO TagTable(Tag) VALUES (:Tag)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case GET_LAST_MESSAGE_ENTRY:
 | 
				
			||||||
 | 
					                return "SELECT MAX(MsgTable.EntryNum) FROM MsgTable";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case GET_BATCH_MESSAGE_ENTRY:
 | 
				
			||||||
 | 
					                return MessageFormat.format(
 | 
				
			||||||
 | 
					                        "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"
 | 
				
			||||||
 | 
					                                + " INNER JOIN SignatureTable ON MsgTable.EntryNum = SignatureTable.EntryNum"
 | 
				
			||||||
 | 
					                                + " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum"
 | 
				
			||||||
 | 
					                                + " INNER JOIN TagTable ON MsgTagTable.TagId = TagTable.TagId"
 | 
				
			||||||
 | 
					                                + " WHERE SignatureTable.SignerId = :{0}"
 | 
				
			||||||
 | 
					                                + " AND TagTable.Tag = :{1}",
 | 
				
			||||||
 | 
					                        QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0),
 | 
				
			||||||
 | 
					                        QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case GET_BATCH_MESSAGE_DATA:
 | 
				
			||||||
 | 
					                return MessageFormat.format(
 | 
				
			||||||
 | 
					                        "SELECT Data FROM BatchTable"
 | 
				
			||||||
 | 
					                                + " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}"
 | 
				
			||||||
 | 
					                                + " ORDER BY SerialNum ASC",
 | 
				
			||||||
 | 
					                        QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
 | 
				
			||||||
 | 
					                        QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),
 | 
				
			||||||
 | 
					                        QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case INSERT_BATCH_DATA:
 | 
				
			||||||
 | 
					                return MessageFormat.format(
 | 
				
			||||||
 | 
					                        "INSERT INTO BatchTable (SignerId, BatchId, SerialNum, Data)"
 | 
				
			||||||
 | 
					                                + " VALUES (:{0}, :{1}, :{2}, :{3})",
 | 
				
			||||||
 | 
					                        QueryType.INSERT_BATCH_DATA.getParamName(0),
 | 
				
			||||||
 | 
					                        QueryType.INSERT_BATCH_DATA.getParamName(1),
 | 
				
			||||||
 | 
					                        QueryType.INSERT_BATCH_DATA.getParamName(2),
 | 
				
			||||||
 | 
					                        QueryType.INSERT_BATCH_DATA.getParamName(3));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case CHECK_BATCH_LENGTH:
 | 
				
			||||||
 | 
					                return MessageFormat.format(
 | 
				
			||||||
 | 
					                        "SELECT COUNT(Data) AS BatchLength FROM BatchTable"
 | 
				
			||||||
 | 
					                                + " WHERE SignerId = :{0} AND BatchId = :{1}",
 | 
				
			||||||
 | 
					                        QueryType.CHECK_BATCH_LENGTH.getParamName(0),
 | 
				
			||||||
 | 
					                        QueryType.CHECK_BATCH_LENGTH.getParamName(1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case CONNECT_BATCH_TAG:
 | 
				
			||||||
 | 
					                return MessageFormat.format(
 | 
				
			||||||
 | 
					                        "INSERT INTO BatchTagTable (SignerId, BatchId, TagId) SELECT :{0}, :{1}, TagId FROM TagTable"
 | 
				
			||||||
 | 
					                                + " WHERE Tag = :{2}",
 | 
				
			||||||
 | 
					                        QueryType.CONNECT_BATCH_TAG.getParamName(0),
 | 
				
			||||||
 | 
					                        QueryType.CONNECT_BATCH_TAG.getParamName(1),
 | 
				
			||||||
 | 
					                        QueryType.CONNECT_BATCH_TAG.getParamName(2));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case GET_BATCH_TAGS:
 | 
				
			||||||
 | 
					                return MessageFormat.format(
 | 
				
			||||||
 | 
					                        "SELECT Tag FROM TagTable INNER JOIN BatchTagTable ON TagTable.TagId = BatchTagTable.TagId"
 | 
				
			||||||
 | 
					                                + " WHERE SignerId = :{0} AND BatchId = :{1} ORDER BY Tag ASC",
 | 
				
			||||||
 | 
					                        QueryType.GET_BATCH_TAGS.getParamName(0),
 | 
				
			||||||
 | 
					                        QueryType.GET_BATCH_TAGS.getParamName(1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case REMOVE_BATCH_TAGS:
 | 
				
			||||||
 | 
					                return MessageFormat.format(
 | 
				
			||||||
 | 
					                        "DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}",
 | 
				
			||||||
 | 
					                        QueryType.REMOVE_BATCH_TAGS.getParamName(0),
 | 
				
			||||||
 | 
					                        QueryType.REMOVE_BATCH_TAGS.getParamName(1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
           default:
 | 
					           default:
 | 
				
			||||||
               throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
 | 
					               throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -52,21 +131,34 @@ public class SQLiteQueryProvider implements BulletinBoardSQLServer.SQLQueryProvi
 | 
				
			||||||
        switch(filterType) {
 | 
					        switch(filterType) {
 | 
				
			||||||
            case EXACT_ENTRY:
 | 
					            case EXACT_ENTRY:
 | 
				
			||||||
                return "MsgTable.EntryNum = :EntryNum" + serialString;
 | 
					                return "MsgTable.EntryNum = :EntryNum" + serialString;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case MAX_ENTRY:
 | 
					            case MAX_ENTRY:
 | 
				
			||||||
                return "MsgTable.EntryNum <= :EntryNum" + serialString;
 | 
					                return "MsgTable.EntryNum <= :EntryNum" + serialString;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case MIN_ENTRY:
 | 
					            case MIN_ENTRY:
 | 
				
			||||||
                return "MsgTable.EntryNum <= :EntryNum" + serialString;
 | 
					                return "MsgTable.EntryNum <= :EntryNum" + serialString;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case MAX_MESSAGES:
 | 
					            case MAX_MESSAGES:
 | 
				
			||||||
                return "LIMIT = :Limit" + serialString;
 | 
					                return "LIMIT = :Limit" + serialString;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case MSG_ID:
 | 
					            case MSG_ID:
 | 
				
			||||||
                return "MsgTable.MsgId = :MsgId" + serialString;
 | 
					                return "MsgTable.MsgId = :MsgId" + serialString;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case SIGNER_ID:
 | 
					            case SIGNER_ID:
 | 
				
			||||||
                return "EXISTS (SELECT 1 FROM SignatureTable"
 | 
					                return "EXISTS (SELECT 1 FROM SignatureTable"
 | 
				
			||||||
                        + " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)";
 | 
					                        + " WHERE SignatureTable.SignerId = :SignerId" + serialString + " AND SignatureTable.EntryNum = MsgTable.EntryNum)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case TAG:
 | 
					            case TAG:
 | 
				
			||||||
                return "EXISTS (SELECT 1 FROM TagTable"
 | 
					                return "EXISTS (SELECT 1 FROM TagTable"
 | 
				
			||||||
                        + " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId"
 | 
					                        + " INNER JOIN MsgTagTable ON TagTable.TagId = MsgTagTable.TagId"
 | 
				
			||||||
                        + " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)";
 | 
					                        + " WHERE TagTable.Tag = :Tag" + serialString + " AND MsgTagTable.EntryNum = MsgTable.EntryNum)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case BEFORE_TIME:
 | 
				
			||||||
 | 
					                return "MsgTable.ExactTime <= :TimeStamp";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case AFTER_TIME:
 | 
				
			||||||
 | 
					                return "MsgTable.ExactTime >= :TimeStamp";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
                throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
 | 
					                throw new IllegalArgumentException("Cannot serve a filter of type " + filterType);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,9 +24,12 @@ import meerkat.comm.CommunicationException;
 | 
				
			||||||
import meerkat.comm.MessageInputStream;
 | 
					import meerkat.comm.MessageInputStream;
 | 
				
			||||||
import meerkat.comm.MessageOutputStream;
 | 
					import meerkat.comm.MessageOutputStream;
 | 
				
			||||||
import meerkat.comm.MessageInputStream.MessageInputStreamFactory;
 | 
					import meerkat.comm.MessageInputStream.MessageInputStreamFactory;
 | 
				
			||||||
 | 
					import meerkat.crypto.Digest;
 | 
				
			||||||
import meerkat.crypto.concrete.ECDSASignature;
 | 
					import meerkat.crypto.concrete.ECDSASignature;
 | 
				
			||||||
 | 
					import meerkat.crypto.concrete.SHA256Digest;
 | 
				
			||||||
import meerkat.protobuf.BulletinBoardAPI.*;
 | 
					import meerkat.protobuf.BulletinBoardAPI.*;
 | 
				
			||||||
import meerkat.util.BulletinBoardUtils;
 | 
					import meerkat.util.BulletinBoardMessageGenerator;
 | 
				
			||||||
 | 
					import org.h2.util.DateTimeUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.junit.Assert.*;
 | 
					import static org.junit.Assert.*;
 | 
				
			||||||
import static org.hamcrest.CoreMatchers.*;
 | 
					import static org.hamcrest.CoreMatchers.*;
 | 
				
			||||||
| 
						 | 
					@ -59,6 +62,10 @@ public class GenericBulletinBoardServerTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
 | 
						private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private BulletinBoardMessageGenerator bulletinBoardMessageGenerator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private Digest digest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * @param bulletinBoardServer is an initialized server.
 | 
						 * @param bulletinBoardServer is an initialized server.
 | 
				
			||||||
	 * @throws InstantiationException
 | 
						 * @throws InstantiationException
 | 
				
			||||||
| 
						 | 
					@ -121,8 +128,12 @@ public class GenericBulletinBoardServerTest {
 | 
				
			||||||
			System.err.println("Couldn't find signing key " + e.getMessage());
 | 
								System.err.println("Couldn't find signing key " + e.getMessage());
 | 
				
			||||||
			fail("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
 | 
							// We use insecure randomness in tests for repeatability
 | 
				
			||||||
 | 
					        random = new Random(0);
 | 
				
			||||||
 | 
							bulletinBoardMessageGenerator = new BulletinBoardMessageGenerator(random);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							digest = new SHA256Digest();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		long end = threadBean.getCurrentThreadCpuTime();
 | 
							long end = threadBean.getCurrentThreadCpuTime();
 | 
				
			||||||
		System.err.println("Finished initializing GenericBulletinBoardServerTest");
 | 
							System.err.println("Finished initializing GenericBulletinBoardServerTest");
 | 
				
			||||||
| 
						 | 
					@ -182,7 +193,10 @@ public class GenericBulletinBoardServerTest {
 | 
				
			||||||
		for (i = 1; i <= MESSAGE_NUM; i++) {
 | 
							for (i = 1; i <= MESSAGE_NUM; i++) {
 | 
				
			||||||
			unsignedMsgBuilder = UnsignedBulletinBoardMessage.newBuilder()
 | 
								unsignedMsgBuilder = UnsignedBulletinBoardMessage.newBuilder()
 | 
				
			||||||
					.setData(ByteString.copyFrom(data[i - 1]))
 | 
										.setData(ByteString.copyFrom(data[i - 1]))
 | 
				
			||||||
					.setTimestamp(BulletinBoardUtils.toTimestampProto());
 | 
										.setTimestamp(Timestamp.newBuilder()
 | 
				
			||||||
 | 
												.setSeconds(i)
 | 
				
			||||||
 | 
												.setNanos(i)
 | 
				
			||||||
 | 
												.build());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Add tags based on bit-representation of message number.
 | 
								// Add tags based on bit-representation of message number.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -619,6 +633,74 @@ public class GenericBulletinBoardServerTest {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void testSyncQuery()
 | 
				
			||||||
 | 
								throws SignatureException, CommunicationException, IOException,NoSuchMethodException, IllegalAccessException, InvocationTargetException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Checksum checksum = new SimpleChecksum();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Timestamp timestamp = Timestamp.newBuilder()
 | 
				
			||||||
 | 
									.setSeconds(1)
 | 
				
			||||||
 | 
									.setNanos(0)
 | 
				
			||||||
 | 
									.build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							BulletinBoardMessage newMessage = bulletinBoardMessageGenerator.generateRandomMessage(signers, timestamp, 10, 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							BoolMsg result = bulletinBoardServer.postMessage(newMessage);
 | 
				
			||||||
 | 
							assertThat("Failed to post message to BB Server", result.getValue(), is(true));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							digest.update(newMessage.getMsg());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ByteString messageID = ByteString.copyFrom(digest.digest());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							MessageFilterList filterList = MessageFilterList.newBuilder()
 | 
				
			||||||
 | 
									.addFilter(MessageFilter.newBuilder()
 | 
				
			||||||
 | 
											.setType(FilterType.MSG_ID)
 | 
				
			||||||
 | 
											.setId(messageID)
 | 
				
			||||||
 | 
											.build())
 | 
				
			||||||
 | 
									.build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bulletinBoardServer.readMessages(filterList, new MessageOutputStream<BulletinBoardMessage>(outputStream));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							MessageInputStream<BulletinBoardMessage> inputStream =
 | 
				
			||||||
 | 
									MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream(
 | 
				
			||||||
 | 
											outputStream.toByteArray()),
 | 
				
			||||||
 | 
											BulletinBoardMessage.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							long lastEntry = inputStream.asList().get(0).getEntryNum();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							SyncQuery syncQuery = SyncQuery.newBuilder()
 | 
				
			||||||
 | 
									.setFilterList(MessageFilterList.getDefaultInstance())
 | 
				
			||||||
 | 
									.addQuery(SingleSyncQuery.newBuilder()
 | 
				
			||||||
 | 
											.setChecksum(2)
 | 
				
			||||||
 | 
											.setTimeOfSync(Timestamp.newBuilder()
 | 
				
			||||||
 | 
													.setSeconds(2)
 | 
				
			||||||
 | 
													.setNanos(0)
 | 
				
			||||||
 | 
													.build())
 | 
				
			||||||
 | 
											.build())
 | 
				
			||||||
 | 
									.build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							SyncQueryResponse queryResponse = bulletinBoardServer.querySync(syncQuery);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat("Sync query replies with positive sync when no sync was expected", queryResponse.getLastEntryNum(), is(equalTo(-1l)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							syncQuery = SyncQuery.newBuilder()
 | 
				
			||||||
 | 
									.setFilterList(MessageFilterList.getDefaultInstance())
 | 
				
			||||||
 | 
									.addQuery(SingleSyncQuery.newBuilder()
 | 
				
			||||||
 | 
											.setChecksum(checksum.getChecksum(messageID))
 | 
				
			||||||
 | 
											.setTimeOfSync(timestamp)
 | 
				
			||||||
 | 
											.build())
 | 
				
			||||||
 | 
									.build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							queryResponse = bulletinBoardServer.querySync(syncQuery);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat("Sync query reply contained wrong last entry number", lastEntry, is(equalTo(queryResponse.getLastEntryNum())));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat("Sync query reply contained wrong timestamp", timestamp, is(equalTo(queryResponse.getLastTimeOfSync())));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public void close(){
 | 
						public void close(){
 | 
				
			||||||
		signers[0].clearSigningKey();
 | 
							signers[0].clearSigningKey();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,8 +8,11 @@ import org.junit.After;
 | 
				
			||||||
import org.junit.Before;
 | 
					import org.junit.Before;
 | 
				
			||||||
import org.junit.Test;
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
import java.lang.management.ManagementFactory;
 | 
					import java.lang.management.ManagementFactory;
 | 
				
			||||||
import java.lang.management.ThreadMXBean;
 | 
					import java.lang.management.ThreadMXBean;
 | 
				
			||||||
 | 
					import java.lang.reflect.InvocationTargetException;
 | 
				
			||||||
 | 
					import java.security.SignatureException;
 | 
				
			||||||
import java.sql.Connection;
 | 
					import java.sql.Connection;
 | 
				
			||||||
import java.sql.DriverManager;
 | 
					import java.sql.DriverManager;
 | 
				
			||||||
import java.sql.SQLException;
 | 
					import java.sql.SQLException;
 | 
				
			||||||
| 
						 | 
					@ -144,6 +147,16 @@ public class MySQLBulletinBoardServerTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void testSyncQuery() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            serverTest.testSyncQuery();
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            System.err.println(e.getMessage());
 | 
				
			||||||
 | 
					            fail(e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @After
 | 
					    @After
 | 
				
			||||||
    public void close() {
 | 
					    public void close() {
 | 
				
			||||||
        System.err.println("Starting to close MySQLBulletinBoardServerTest");
 | 
					        System.err.println("Starting to close MySQLBulletinBoardServerTest");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,122 @@
 | 
				
			||||||
 | 
					package meerkat.bulletinboard;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.protobuf.ByteString;
 | 
				
			||||||
 | 
					import meerkat.crypto.Digest;
 | 
				
			||||||
 | 
					import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
 | 
				
			||||||
 | 
					import meerkat.protobuf.BulletinBoardAPI.MessageID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Collection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Created by Arbel Deutsch Peled on 01-Mar-16.
 | 
				
			||||||
 | 
					 * This interface is used to create checksums of Bulletin Board messages IDs
 | 
				
			||||||
 | 
					 * This is useful in comparing database states
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface Checksum {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the Digest method which is used in creating message IDs from the messages themselves
 | 
				
			||||||
 | 
					     * This method must be called with an initialized Digest before calling any methods that receive a parameter of type BulletinBoardMessage
 | 
				
			||||||
 | 
					     * @param digest is the Digest that will be used to create message IDs from Bulletin Board Messages
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setDigest(Digest digest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Used to reset the current checksum state
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Update the current checksum with the given ID
 | 
				
			||||||
 | 
					     * @param messageID is the message ID to be added
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void update(MessageID messageID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Update the current checksum with the given collection of IDs
 | 
				
			||||||
 | 
					     * @param messageIDs contains the message IDs
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void update(Collection<MessageID> messageIDs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Update the current checksum with the given ID
 | 
				
			||||||
 | 
					     * @param messageID is the message ID to be added
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void update(ByteString messageID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Update the current checksum with the given ID
 | 
				
			||||||
 | 
					     * @param messageID is the message ID to be added
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void update(byte[] messageID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Update the current checksum with the message ID of the given message
 | 
				
			||||||
 | 
					     * @param bulletinBoardMessage is the message whose ID should be added to the checksum
 | 
				
			||||||
 | 
					     * @throws IllegalStateException if a Digest has not been set before calling this method
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void digestAndUpdate(BulletinBoardMessage bulletinBoardMessage) throws IllegalStateException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Update the current checksum with the message IDs of the given messages
 | 
				
			||||||
 | 
					     * @param bulletinBoardMessages contains the messages whose IDs should be added to the checksum
 | 
				
			||||||
 | 
					     * @throws IllegalStateException if a Digest has not been set before calling this method
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void digestAndUpdate(Collection<BulletinBoardMessage> bulletinBoardMessages) throws IllegalStateException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the current checksum without changing the checksum state
 | 
				
			||||||
 | 
					     * @return the current checksum
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public long getChecksum();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Updates the current checksum with the given ID and returns the resulting checksum
 | 
				
			||||||
 | 
					     * The checksum is not reset afterwards
 | 
				
			||||||
 | 
					     * @param messageID is the message ID to be added
 | 
				
			||||||
 | 
					     * @return the updated checksum
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public long getChecksum(MessageID messageID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Updates the current checksum with the given ID and returns the resulting checksum
 | 
				
			||||||
 | 
					     * The checksum is not reset afterwards
 | 
				
			||||||
 | 
					     * @param messageID is the message ID to be added
 | 
				
			||||||
 | 
					     * @return the updated checksum
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public long getChecksum(ByteString messageID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Updates the current checksum with the given ID and returns the resulting checksum
 | 
				
			||||||
 | 
					     * The checksum is not reset afterwards
 | 
				
			||||||
 | 
					     * @param messageID is the message ID to be added
 | 
				
			||||||
 | 
					     * @return the updated checksum
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public long getChecksum(byte[] messageID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Updates the current checksum with the given IDs and returns the resulting checksum
 | 
				
			||||||
 | 
					     * The checksum is not reset afterwards
 | 
				
			||||||
 | 
					     * @param messageIDs contains the message IDs to be added
 | 
				
			||||||
 | 
					     * @return the updated checksum
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public long getChecksum(Collection<MessageID> messageIDs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Updates the current checksum with the message ID of the given message
 | 
				
			||||||
 | 
					     * The checksum is not reset afterwards
 | 
				
			||||||
 | 
					     * @param bulletinBoardMessage is the message whose ID should be added to the checksum
 | 
				
			||||||
 | 
					     * @return the updated checksum
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public long digestAndGetChecksum(BulletinBoardMessage bulletinBoardMessage) throws IllegalStateException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Updates the current checksum with the message IDs of the given messages
 | 
				
			||||||
 | 
					     * The checksum is not reset afterwards
 | 
				
			||||||
 | 
					     * @param bulletinBoardMessages contains the messages whose IDs should be added to the checksum
 | 
				
			||||||
 | 
					     * @return the updated checksum
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public long digestAndGetChecksum(Collection<BulletinBoardMessage> bulletinBoardMessages) throws IllegalStateException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -29,6 +29,8 @@ public class GenericBatchDigest implements BatchDigest{
 | 
				
			||||||
            update(batchData);
 | 
					            update(batchData);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        update(completeBatch.getTimestamp());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,10 +32,13 @@ public class GenericBatchDigitalSignature implements BatchDigitalSignature{
 | 
				
			||||||
    public void updateContent(CompleteBatch completeBatch) throws SignatureException {
 | 
					    public void updateContent(CompleteBatch completeBatch) throws SignatureException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        digitalSignature.updateContent(completeBatch.getBeginBatchMessage());
 | 
					        digitalSignature.updateContent(completeBatch.getBeginBatchMessage());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (BatchData batchData : completeBatch.getBatchDataList()) {
 | 
					        for (BatchData batchData : completeBatch.getBatchDataList()) {
 | 
				
			||||||
            digitalSignature.updateContent(batchData);
 | 
					            digitalSignature.updateContent(batchData);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        digitalSignature.updateContent(completeBatch.getTimestamp());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,131 @@
 | 
				
			||||||
 | 
					package meerkat.bulletinboard;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.protobuf.ByteString;
 | 
				
			||||||
 | 
					import meerkat.crypto.Digest;
 | 
				
			||||||
 | 
					import meerkat.protobuf.BulletinBoardAPI.MessageID;
 | 
				
			||||||
 | 
					import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Collection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Created by Arbel Deutsch Peled on 01-Mar-16.
 | 
				
			||||||
 | 
					 * Implementation of Checksum via bitwise XOR of the bytes of message IDs
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class SimpleChecksum implements Checksum{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private Digest digest;
 | 
				
			||||||
 | 
					    private long checksum;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public SimpleChecksum() {
 | 
				
			||||||
 | 
					        digest = null;
 | 
				
			||||||
 | 
					        reset();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setDigest(Digest digest) {
 | 
				
			||||||
 | 
					        this.digest = digest;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void reset() {
 | 
				
			||||||
 | 
					        checksum = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void update(MessageID messageID) {
 | 
				
			||||||
 | 
					        ByteString messageIDByteString = messageID.getID();
 | 
				
			||||||
 | 
					        update(messageIDByteString);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void update(Collection<MessageID> messageIDs) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (MessageID messageID : messageIDs){
 | 
				
			||||||
 | 
					            update(messageID);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void update(ByteString messageID) {
 | 
				
			||||||
 | 
					        for (int i = 0 ; i < messageID.size() ; i++){
 | 
				
			||||||
 | 
					            checksum &= messageID.byteAt(i);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void update(byte[] messageID) {
 | 
				
			||||||
 | 
					        for (int i = 0 ; i < messageID.length ; i++){
 | 
				
			||||||
 | 
					            checksum &= messageID[i];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void checkDigest() throws IllegalStateException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (digest == null){
 | 
				
			||||||
 | 
					            throw new IllegalStateException("Digest method not set. Use setDigest method before calling digestAndUpdate.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void digestAndUpdate(BulletinBoardMessage bulletinBoardMessage) throws IllegalStateException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        checkDigest();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        digest.reset();
 | 
				
			||||||
 | 
					        digest.update(bulletinBoardMessage);
 | 
				
			||||||
 | 
					        update(digest.digest());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void digestAndUpdate(Collection<BulletinBoardMessage> bulletinBoardMessages) throws IllegalStateException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (BulletinBoardMessage bulletinBoardMessage : bulletinBoardMessages){
 | 
				
			||||||
 | 
					            digestAndUpdate(bulletinBoardMessage);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public long getChecksum() {
 | 
				
			||||||
 | 
					        return checksum;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public long getChecksum(MessageID messageID) {
 | 
				
			||||||
 | 
					        update(messageID);
 | 
				
			||||||
 | 
					        return getChecksum();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public long getChecksum(ByteString messageID) {
 | 
				
			||||||
 | 
					        update(messageID);
 | 
				
			||||||
 | 
					        return getChecksum();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public long getChecksum(byte[] messageID) {
 | 
				
			||||||
 | 
					        update(messageID);
 | 
				
			||||||
 | 
					        return getChecksum();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public long getChecksum(Collection<MessageID> messageIDs) {
 | 
				
			||||||
 | 
					        update(messageIDs);
 | 
				
			||||||
 | 
					        return getChecksum();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public long digestAndGetChecksum(BulletinBoardMessage bulletinBoardMessage) throws IllegalStateException {
 | 
				
			||||||
 | 
					        digestAndUpdate(bulletinBoardMessage);
 | 
				
			||||||
 | 
					        return getChecksum();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public long digestAndGetChecksum(Collection<BulletinBoardMessage> bulletinBoardMessages) throws IllegalStateException {
 | 
				
			||||||
 | 
					        digestAndUpdate(bulletinBoardMessages);
 | 
				
			||||||
 | 
					        return getChecksum();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ import com.google.protobuf.Message;
 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
import java.io.InputStream;
 | 
					import java.io.InputStream;
 | 
				
			||||||
import java.lang.reflect.InvocationTargetException;
 | 
					import java.lang.reflect.InvocationTargetException;
 | 
				
			||||||
 | 
					import java.util.Iterator;
 | 
				
			||||||
import java.util.LinkedList;
 | 
					import java.util.LinkedList;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +13,7 @@ import java.util.List;
 | 
				
			||||||
 * Created by Arbel Deutsch Peled on 21-Feb-16.
 | 
					 * Created by Arbel Deutsch Peled on 21-Feb-16.
 | 
				
			||||||
 * A input stream of Protobuf messages
 | 
					 * A input stream of Protobuf messages
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class MessageInputStream<T extends Message>{
 | 
					public class MessageInputStream<T extends Message> implements Iterable<T>{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private T.Builder builder;
 | 
					    private T.Builder builder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +24,33 @@ public class MessageInputStream<T extends Message>{
 | 
				
			||||||
        this.builder = (T.Builder) type.getMethod("newBuilder").invoke(type);
 | 
					        this.builder = (T.Builder) type.getMethod("newBuilder").invoke(type);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public Iterator<T> iterator() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new Iterator<T>() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public boolean hasNext() {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    return isAvailable();
 | 
				
			||||||
 | 
					                } catch (IOException e) {
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public T next() {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    return readMessage();
 | 
				
			||||||
 | 
					                } catch (IOException e) {
 | 
				
			||||||
 | 
					                    return null;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Factory class for actually creating a MessageInputStream
 | 
					     * Factory class for actually creating a MessageInputStream
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
| 
						 | 
					@ -61,4 +89,10 @@ public class MessageInputStream<T extends Message>{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void close() throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        in.close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,4 +21,8 @@ public class MessageOutputStream<T extends Message> {
 | 
				
			||||||
        message.writeDelimitedTo(out);
 | 
					        message.writeDelimitedTo(out);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void close() throws IOException {
 | 
				
			||||||
 | 
					        out.close();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -80,7 +80,7 @@ public class BulletinBoardUtils {
 | 
				
			||||||
     * This method creates a Timestamp Protobuf from the current system time
 | 
					     * This method creates a Timestamp Protobuf from the current system time
 | 
				
			||||||
     * @return a Timestamp Protobuf encoding of the current system time
 | 
					     * @return a Timestamp Protobuf encoding of the current system time
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static com.google.protobuf.Timestamp toTimestampProto() {
 | 
					    public static com.google.protobuf.Timestamp getCurrentTimestampProto() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return toTimestampProto(System.currentTimeMillis());
 | 
					        return toTimestampProto(System.currentTimeMillis());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue