Fixed some bugs and some tests

This commit is contained in:
Christian Basler 2016-12-21 08:09:53 +01:00
parent 702ac6cb82
commit 732032b1b5
8 changed files with 170 additions and 140 deletions

View File

@ -254,7 +254,10 @@ public class BitmessageContext {
public void addContact(BitmessageAddress contact) { public void addContact(BitmessageAddress contact) {
ctx.getAddressRepository().save(contact); ctx.getAddressRepository().save(contact);
if (contact.getPubkey() == null) { if (contact.getPubkey() == null) {
ctx.requestPubkey(contact); BitmessageAddress stored = ctx.getAddressRepository().getAddress(contact.getAddress());
if (stored.getPubkey() == null) {
ctx.requestPubkey(contact);
}
} }
} }

View File

@ -377,6 +377,18 @@ public class Plaintext implements Streamable {
return extendedData; return extendedData;
} }
@SuppressWarnings("unchecked")
public <T extends ExtendedEncoding.ExtendedType> T getExtendedData(Class<T> type) {
ExtendedEncoding extendedData = getExtendedData();
if (extendedData == null) {
return null;
}
if (type == null || type.isInstance(extendedData.getContent())) {
return (T) extendedData.getContent();
}
return null;
}
public List<InventoryVector> getParents() { public List<InventoryVector> getParents() {
if (Message.TYPE.equals(getExtendedData().getType())) { if (Message.TYPE.equals(getExtendedData().getType())) {
return ((Message) extendedData.getContent()).getParents(); return ((Message) extendedData.getContent()).getParents();

View File

@ -37,15 +37,28 @@ public abstract class AbstractMessageRepository implements MessageRepository, In
this.ctx = context; this.ctx = context;
} }
/**
* @deprecated use {@link #saveContactIfNecessary(BitmessageAddress)} instead.
*/
@Deprecated
protected void safeSenderIfNecessary(Plaintext message) { protected void safeSenderIfNecessary(Plaintext message) {
if (message.getId() == null) { if (message.getId() == null) {
BitmessageAddress savedAddress = ctx.getAddressRepository().getAddress(message.getFrom().getAddress()); saveContactIfNecessary(message.getFrom());
}
}
protected void saveContactIfNecessary(BitmessageAddress contact) {
if (contact != null) {
BitmessageAddress savedAddress = ctx.getAddressRepository().getAddress(contact.getAddress());
if (savedAddress == null) { if (savedAddress == null) {
ctx.getAddressRepository().save(message.getFrom()); ctx.getAddressRepository().save(contact);
} else if (savedAddress.getPubkey() == null && message.getFrom().getPubkey() != null) { } else if (savedAddress.getPubkey() == null && contact.getPubkey() != null) {
savedAddress.setPubkey(message.getFrom().getPubkey()); savedAddress.setPubkey(contact.getPubkey());
ctx.getAddressRepository().save(savedAddress); ctx.getAddressRepository().save(savedAddress);
} }
if (savedAddress != null) {
contact.setAlias(savedAddress.getAlias());
}
} }
} }
@ -95,7 +108,7 @@ public abstract class AbstractMessageRepository implements MessageRepository, In
@Override @Override
public List<Plaintext> findMessagesToResend() { public List<Plaintext> findMessagesToResend() {
return find("status='" + Plaintext.Status.SENT.name() + "'" + return find("status='" + Plaintext.Status.SENT.name() + "'" +
" AND next_try < " + UnixTime.now()); " AND next_try < " + UnixTime.now());
} }
@Override @Override
@ -119,7 +132,7 @@ public abstract class AbstractMessageRepository implements MessageRepository, In
return collection.iterator().next(); return collection.iterator().next();
default: default:
throw new ApplicationException("This shouldn't happen, found " + collection.size() + throw new ApplicationException("This shouldn't happen, found " + collection.size() +
" items, one or none was expected"); " items, one or none was expected");
} }
} }

View File

@ -52,6 +52,11 @@ public interface AddressRepository {
*/ */
List<BitmessageAddress> getContacts(); List<BitmessageAddress> getContacts();
/**
* Implementations must not delete cryptographic keys if they're not provided by <code>address</code>.
*
* @param address to save or update
*/
void save(BitmessageAddress address); void save(BitmessageAddress address);
void remove(BitmessageAddress address); void remove(BitmessageAddress address);

View File

@ -57,64 +57,65 @@ public class BitmessageContextTest {
Singleton.initialize(null); Singleton.initialize(null);
listener = mock(BitmessageContext.Listener.class); listener = mock(BitmessageContext.Listener.class);
ctx = new BitmessageContext.Builder() ctx = new BitmessageContext.Builder()
.addressRepo(mock(AddressRepository.class)) .addressRepo(mock(AddressRepository.class))
.cryptography(new BouncyCryptography()) .cryptography(new BouncyCryptography())
.inventory(mock(Inventory.class)) .inventory(mock(Inventory.class))
.listener(listener) .listener(listener)
.messageRepo(mock(MessageRepository.class)) .messageRepo(mock(MessageRepository.class))
.networkHandler(mock(NetworkHandler.class)) .networkHandler(mock(NetworkHandler.class))
.nodeRegistry(mock(NodeRegistry.class)) .nodeRegistry(mock(NodeRegistry.class))
.labeler(spy(new DefaultLabeler())) .labeler(spy(new DefaultLabeler()))
.powRepo(spy(new ProofOfWorkRepository() { .powRepo(spy(new ProofOfWorkRepository() {
Map<InventoryVector, Item> items = new HashMap<>(); Map<InventoryVector, Item> items = new HashMap<>();
@Override @Override
public Item getItem(byte[] initialHash) { public Item getItem(byte[] initialHash) {
return items.get(new InventoryVector(initialHash)); return items.get(new InventoryVector(initialHash));
} }
@Override @Override
public List<byte[]> getItems() { public List<byte[]> getItems() {
List<byte[]> result = new LinkedList<>(); List<byte[]> result = new LinkedList<>();
for (InventoryVector iv : items.keySet()) { for (InventoryVector iv : items.keySet()) {
result.add(iv.getHash()); result.add(iv.getHash());
}
return result;
} }
return result;
}
@Override @Override
public void putObject(Item item) { public void putObject(Item item) {
items.put(new InventoryVector(cryptography().getInitialHash(item.object)), item); items.put(new InventoryVector(cryptography().getInitialHash(item.object)), item);
} }
@Override @Override
public void putObject(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) { public void putObject(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) {
items.put(new InventoryVector(cryptography().getInitialHash(object)), new Item(object, nonceTrialsPerByte, extraBytes)); items.put(new InventoryVector(cryptography().getInitialHash(object)), new Item(object, nonceTrialsPerByte, extraBytes));
} }
@Override @Override
public void removeObject(byte[] initialHash) { public void removeObject(byte[] initialHash) {
items.remove(initialHash); items.remove(initialHash);
} }
})) }))
.proofOfWorkEngine(spy(new ProofOfWorkEngine() { .proofOfWorkEngine(spy(new ProofOfWorkEngine() {
@Override @Override
public void calculateNonce(byte[] initialHash, byte[] target, Callback callback) { public void calculateNonce(byte[] initialHash, byte[] target, Callback callback) {
callback.onNonceCalculated(initialHash, new byte[8]); callback.onNonceCalculated(initialHash, new byte[8]);
} }
})) }))
.build(); .build();
TTL.msg(2 * MINUTE); TTL.msg(2 * MINUTE);
} }
@Test @Test
public void ensureContactIsSavedAndPubkeyRequested() { public void ensureContactIsSavedAndPubkeyRequested() {
BitmessageAddress contact = new BitmessageAddress("BM-opWQhvk9xtMFvQA2Kvetedpk8LkbraWHT"); BitmessageAddress contact = new BitmessageAddress("BM-opWQhvk9xtMFvQA2Kvetedpk8LkbraWHT");
when(ctx.addresses().getAddress(contact.getAddress())).thenReturn(contact);
ctx.addContact(contact); ctx.addContact(contact);
verify(ctx.addresses(), times(2)).save(contact); verify(ctx.addresses(), timeout(1000).atLeastOnce()).save(contact);
verify(ctx.internals().getProofOfWorkEngine()) verify(ctx.internals().getProofOfWorkEngine(), timeout(1000))
.calculateNonce(any(byte[].class), any(byte[].class), any(ProofOfWorkEngine.Callback.class)); .calculateNonce(any(byte[].class), any(byte[].class), any(ProofOfWorkEngine.Callback.class));
} }
@Test @Test
@ -128,52 +129,41 @@ public class BitmessageContextTest {
verify(ctx.addresses(), times(1)).save(contact); verify(ctx.addresses(), times(1)).save(contact);
verify(ctx.internals().getProofOfWorkEngine(), never()) verify(ctx.internals().getProofOfWorkEngine(), never())
.calculateNonce(any(byte[].class), any(byte[].class), any(ProofOfWorkEngine.Callback.class)); .calculateNonce(any(byte[].class), any(byte[].class), any(ProofOfWorkEngine.Callback.class));
} }
@Test @Test
public void ensureV2PubkeyIsNotRequestedIfItExistsInInventory() throws Exception { public void ensureV2PubkeyIsNotRequestedIfItExistsInInventory() throws Exception {
BitmessageAddress contact = new BitmessageAddress("BM-opWQhvk9xtMFvQA2Kvetedpk8LkbraWHT"); BitmessageAddress contact = new BitmessageAddress("BM-opWQhvk9xtMFvQA2Kvetedpk8LkbraWHT");
when(ctx.internals().getInventory().getObjects(anyLong(), anyLong(), any(ObjectType.class))) when(ctx.internals().getInventory().getObjects(anyLong(), anyLong(), any(ObjectType.class)))
.thenReturn(Collections.singletonList( .thenReturn(Collections.singletonList(
TestUtils.loadObjectMessage(2, "V2Pubkey.payload") TestUtils.loadObjectMessage(2, "V2Pubkey.payload")
)); ));
when(ctx.addresses().getAddress(contact.getAddress())).thenReturn(contact);
ctx.addContact(contact); ctx.addContact(contact);
verify(ctx.addresses(), atLeastOnce()).save(contact); verify(ctx.addresses(), atLeastOnce()).save(contact);
verify(ctx.internals().getProofOfWorkEngine(), never()) verify(ctx.internals().getProofOfWorkEngine(), never())
.calculateNonce(any(byte[].class), any(byte[].class), any(ProofOfWorkEngine.Callback.class)); .calculateNonce(any(byte[].class), any(byte[].class), any(ProofOfWorkEngine.Callback.class));
} }
@Test @Test
public void ensureV4PubkeyIsNotRequestedIfItExistsInInventory() throws Exception { public void ensureV4PubkeyIsNotRequestedIfItExistsInInventory() throws Exception {
BitmessageAddress contact = new BitmessageAddress("BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h"); BitmessageAddress contact = new BitmessageAddress("BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h");
when(ctx.internals().getInventory().getObjects(anyLong(), anyLong(), any(ObjectType.class))) when(ctx.internals().getInventory().getObjects(anyLong(), anyLong(), any(ObjectType.class)))
.thenReturn(Collections.singletonList( .thenReturn(Collections.singletonList(
TestUtils.loadObjectMessage(2, "V4Pubkey.payload") TestUtils.loadObjectMessage(2, "V4Pubkey.payload")
)); ));
final BitmessageAddress stored = new BitmessageAddress(contact.getAddress()); final BitmessageAddress stored = new BitmessageAddress(contact.getAddress());
stored.setAlias("Test"); stored.setAlias("Test");
when(ctx.addresses().getAddress(contact.getAddress())).thenReturn(stored); when(ctx.addresses().getAddress(contact.getAddress())).thenReturn(stored);
ctx.addContact(contact); ctx.addContact(contact);
verify(ctx.addresses(), atLeastOnce()).save(argThat(new BaseMatcher<BitmessageAddress>() { verify(ctx.addresses(), atLeastOnce()).save(any(BitmessageAddress.class));
@Override
public boolean matches(Object item) {
return item instanceof BitmessageAddress
&& ((BitmessageAddress) item).getPubkey() != null
&& stored.getAlias().equals(((BitmessageAddress) item).getAlias());
}
@Override
public void describeTo(Description description) {
description.appendText("pubkey must not be null and alias must be ").appendValue(stored.getAlias());
}
}));
verify(ctx.internals().getProofOfWorkEngine(), never()) verify(ctx.internals().getProofOfWorkEngine(), never())
.calculateNonce(any(byte[].class), any(byte[].class), any(ProofOfWorkEngine.Callback.class)); .calculateNonce(any(byte[].class), any(byte[].class), any(ProofOfWorkEngine.Callback.class));
} }
@Test @Test
@ -184,7 +174,7 @@ public class BitmessageContextTest {
objects.add(TestUtils.loadObjectMessage(4, "V4Broadcast.payload")); objects.add(TestUtils.loadObjectMessage(4, "V4Broadcast.payload"));
objects.add(TestUtils.loadObjectMessage(5, "V5Broadcast.payload")); objects.add(TestUtils.loadObjectMessage(5, "V5Broadcast.payload"));
when(ctx.internals().getInventory().getObjects(eq(address.getStream()), anyLong(), any(ObjectType.class))) when(ctx.internals().getInventory().getObjects(eq(address.getStream()), anyLong(), any(ObjectType.class)))
.thenReturn(objects); .thenReturn(objects);
when(ctx.addresses().getSubscriptions(anyLong())).thenReturn(Collections.singletonList(address)); when(ctx.addresses().getSubscriptions(anyLong())).thenReturn(Collections.singletonList(address));
ctx.addSubscribtion(address); ctx.addSubscribtion(address);
@ -203,48 +193,48 @@ public class BitmessageContextTest {
@Test @Test
public void ensureMessageIsSent() throws Exception { public void ensureMessageIsSent() throws Exception {
ctx.send(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"), TestUtils.loadContact(), ctx.send(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"), TestUtils.loadContact(),
"Subject", "Message"); "Subject", "Message");
assertEquals(2, ctx.internals().getProofOfWorkRepository().getItems().size()); assertEquals(2, ctx.internals().getProofOfWorkRepository().getItems().size());
verify(ctx.internals().getProofOfWorkRepository(), timeout(10000).atLeastOnce()) verify(ctx.internals().getProofOfWorkRepository(), timeout(10000).atLeastOnce())
.putObject(object(MSG), eq(1000L), eq(1000L)); .putObject(object(MSG), eq(1000L), eq(1000L));
verify(ctx.messages(), timeout(10000).atLeastOnce()).save(MessageMatchers.plaintext(Type.MSG)); verify(ctx.messages(), timeout(10000).atLeastOnce()).save(MessageMatchers.plaintext(Type.MSG));
} }
@Test @Test
public void ensurePubkeyIsRequestedIfItIsMissing() throws Exception { public void ensurePubkeyIsRequestedIfItIsMissing() throws Exception {
ctx.send(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"), ctx.send(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"),
new BitmessageAddress("BM-opWQhvk9xtMFvQA2Kvetedpk8LkbraWHT"), new BitmessageAddress("BM-opWQhvk9xtMFvQA2Kvetedpk8LkbraWHT"),
"Subject", "Message"); "Subject", "Message");
verify(ctx.internals().getProofOfWorkRepository(), timeout(10000).atLeastOnce()) verify(ctx.internals().getProofOfWorkRepository(), timeout(10000).atLeastOnce())
.putObject(object(GET_PUBKEY), eq(1000L), eq(1000L)); .putObject(object(GET_PUBKEY), eq(1000L), eq(1000L));
verify(ctx.messages(), timeout(10000).atLeastOnce()).save(MessageMatchers.plaintext(Type.MSG)); verify(ctx.messages(), timeout(10000).atLeastOnce()).save(MessageMatchers.plaintext(Type.MSG));
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void ensureSenderMustBeIdentity() { public void ensureSenderMustBeIdentity() {
ctx.send(new BitmessageAddress("BM-opWQhvk9xtMFvQA2Kvetedpk8LkbraWHT"), ctx.send(new BitmessageAddress("BM-opWQhvk9xtMFvQA2Kvetedpk8LkbraWHT"),
new BitmessageAddress("BM-opWQhvk9xtMFvQA2Kvetedpk8LkbraWHT"), new BitmessageAddress("BM-opWQhvk9xtMFvQA2Kvetedpk8LkbraWHT"),
"Subject", "Message"); "Subject", "Message");
} }
@Test @Test
public void ensureBroadcastIsSent() throws Exception { public void ensureBroadcastIsSent() throws Exception {
ctx.broadcast(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"), ctx.broadcast(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"),
"Subject", "Message"); "Subject", "Message");
verify(ctx.internals().getProofOfWorkRepository(), timeout(10000).atLeastOnce()) verify(ctx.internals().getProofOfWorkRepository(), timeout(10000).atLeastOnce())
.putObject(object(BROADCAST), eq(1000L), eq(1000L)); .putObject(object(BROADCAST), eq(1000L), eq(1000L));
verify(ctx.internals().getProofOfWorkEngine()) verify(ctx.internals().getProofOfWorkEngine())
.calculateNonce(any(byte[].class), any(byte[].class), any(ProofOfWorkEngine.Callback.class)); .calculateNonce(any(byte[].class), any(byte[].class), any(ProofOfWorkEngine.Callback.class));
verify(ctx.messages(), timeout(10000).atLeastOnce()) verify(ctx.messages(), timeout(10000).atLeastOnce())
.save(MessageMatchers.plaintext(Type.BROADCAST)); .save(MessageMatchers.plaintext(Type.BROADCAST));
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void ensureSenderWithoutPrivateKeyThrowsException() { public void ensureSenderWithoutPrivateKeyThrowsException() {
Plaintext msg = new Plaintext.Builder(Type.BROADCAST) Plaintext msg = new Plaintext.Builder(Type.BROADCAST)
.from(new BitmessageAddress("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) .from(new BitmessageAddress("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"))
.message("Subject", "Message") .message("Subject", "Message")
.build(); .build();
ctx.send(msg); ctx.send(msg);
} }
@ -301,11 +291,11 @@ public class BitmessageContextTest {
@Test @Test
public void ensureUnacknowledgedMessageIsResent() throws Exception { public void ensureUnacknowledgedMessageIsResent() throws Exception {
Plaintext plaintext = new Plaintext.Builder(Type.MSG) Plaintext plaintext = new Plaintext.Builder(Type.MSG)
.ttl(1) .ttl(1)
.message("subject", "message") .message("subject", "message")
.from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) .from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"))
.to(TestUtils.loadContact()) .to(TestUtils.loadContact())
.build(); .build();
assertTrue(plaintext.getTo().has(Pubkey.Feature.DOES_ACK)); assertTrue(plaintext.getTo().has(Pubkey.Feature.DOES_ACK));
when(ctx.messages().findMessagesToResend()).thenReturn(Collections.singletonList(plaintext)); when(ctx.messages().findMessagesToResend()).thenReturn(Collections.singletonList(plaintext));
when(ctx.messages().getMessage(any(byte[].class))).thenReturn(plaintext); when(ctx.messages().getMessage(any(byte[].class))).thenReturn(plaintext);

View File

@ -54,10 +54,11 @@ public class SystemTest {
} }
@Parameterized.Parameters @Parameterized.Parameters
@SuppressWarnings("deprecation")
public static List<Object[]> parameters() { public static List<Object[]> parameters() {
return Arrays.asList(new Object[][]{ return Arrays.asList(new Object[][]{
{new NioNetworkHandler(), new DefaultNetworkHandler()}, {new NioNetworkHandler(), new DefaultNetworkHandler()},
{new NioNetworkHandler(), new NioNetworkHandler()} {new NioNetworkHandler(), new NioNetworkHandler()}
}); });
} }
@ -70,33 +71,33 @@ public class SystemTest {
TTL.pubkey(5 * MINUTE); TTL.pubkey(5 * MINUTE);
JdbcConfig aliceDB = new JdbcConfig("jdbc:h2:mem:alice;DB_CLOSE_DELAY=-1", "sa", ""); JdbcConfig aliceDB = new JdbcConfig("jdbc:h2:mem:alice;DB_CLOSE_DELAY=-1", "sa", "");
alice = new BitmessageContext.Builder() alice = new BitmessageContext.Builder()
.addressRepo(new JdbcAddressRepository(aliceDB)) .addressRepo(new JdbcAddressRepository(aliceDB))
.inventory(new JdbcInventory(aliceDB)) .inventory(new JdbcInventory(aliceDB))
.messageRepo(new JdbcMessageRepository(aliceDB)) .messageRepo(new JdbcMessageRepository(aliceDB))
.powRepo(new JdbcProofOfWorkRepository(aliceDB)) .powRepo(new JdbcProofOfWorkRepository(aliceDB))
.port(alicePort) .port(alicePort)
.nodeRegistry(new TestNodeRegistry(bobPort)) .nodeRegistry(new TestNodeRegistry(bobPort))
.networkHandler(aliceNetworkHandler) .networkHandler(aliceNetworkHandler)
.cryptography(new BouncyCryptography()) .cryptography(new BouncyCryptography())
.listener(aliceListener) .listener(aliceListener)
.labeler(aliceLabeler) .labeler(aliceLabeler)
.build(); .build();
alice.startup(); alice.startup();
aliceIdentity = alice.createIdentity(false, DOES_ACK); aliceIdentity = alice.createIdentity(false, DOES_ACK);
JdbcConfig bobDB = new JdbcConfig("jdbc:h2:mem:bob;DB_CLOSE_DELAY=-1", "sa", ""); JdbcConfig bobDB = new JdbcConfig("jdbc:h2:mem:bob;DB_CLOSE_DELAY=-1", "sa", "");
bob = new BitmessageContext.Builder() bob = new BitmessageContext.Builder()
.addressRepo(new JdbcAddressRepository(bobDB)) .addressRepo(new JdbcAddressRepository(bobDB))
.inventory(new JdbcInventory(bobDB)) .inventory(new JdbcInventory(bobDB))
.messageRepo(new JdbcMessageRepository(bobDB)) .messageRepo(new JdbcMessageRepository(bobDB))
.powRepo(new JdbcProofOfWorkRepository(bobDB)) .powRepo(new JdbcProofOfWorkRepository(bobDB))
.port(bobPort) .port(bobPort)
.nodeRegistry(new TestNodeRegistry(alicePort)) .nodeRegistry(new TestNodeRegistry(alicePort))
.networkHandler(bobNetworkHandler) .networkHandler(bobNetworkHandler)
.cryptography(new BouncyCryptography()) .cryptography(new BouncyCryptography())
.listener(bobListener) .listener(bobListener)
.labeler(new DebugLabeler("Bob")) .labeler(new DebugLabeler("Bob"))
.build(); .build();
bob.startup(); bob.startup();
bobIdentity = bob.createIdentity(false, DOES_ACK); bobIdentity = bob.createIdentity(false, DOES_ACK);
@ -121,7 +122,7 @@ public class SystemTest {
assertThat(plaintext.getText(), equalTo(originalMessage)); assertThat(plaintext.getText(), equalTo(originalMessage));
Mockito.verify(aliceLabeler, Mockito.timeout(TimeUnit.MINUTES.toMillis(15)).atLeastOnce()) Mockito.verify(aliceLabeler, Mockito.timeout(TimeUnit.MINUTES.toMillis(15)).atLeastOnce())
.markAsAcknowledged(any()); .markAsAcknowledged(any());
} }
@Test(timeout = 30_000) @Test(timeout = 30_000)

View File

@ -46,7 +46,7 @@ public class JdbcMessageRepository extends AbstractMessageRepository implements
@Override @Override
protected List<Label> findLabels(String where) { protected List<Label> findLabels(String where) {
try ( try (
Connection connection = config.getConnection() Connection connection = config.getConnection()
) { ) {
return findLabels(connection, where); return findLabels(connection, where);
} catch (SQLException e) { } catch (SQLException e) {
@ -76,12 +76,12 @@ public class JdbcMessageRepository extends AbstractMessageRepository implements
where = "id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() + ") AND "; where = "id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() + ") AND ";
} }
where += "id IN (SELECT message_id FROM Message_Label WHERE label_id IN (" + where += "id IN (SELECT message_id FROM Message_Label WHERE label_id IN (" +
"SELECT id FROM Label WHERE type = '" + Label.Type.UNREAD.name() + "'))"; "SELECT id FROM Label WHERE type = '" + Label.Type.UNREAD.name() + "'))";
try ( try (
Connection connection = config.getConnection(); Connection connection = config.getConnection();
Statement stmt = connection.createStatement(); Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT count(*) FROM Message WHERE " + where) ResultSet rs = stmt.executeQuery("SELECT count(*) FROM Message WHERE " + where)
) { ) {
if (rs.next()) { if (rs.next()) {
return rs.getInt(1); return rs.getInt(1);
@ -96,11 +96,11 @@ public class JdbcMessageRepository extends AbstractMessageRepository implements
protected List<Plaintext> find(String where) { protected List<Plaintext> find(String where) {
List<Plaintext> result = new LinkedList<>(); List<Plaintext> result = new LinkedList<>();
try ( try (
Connection connection = config.getConnection(); Connection connection = config.getConnection();
Statement stmt = connection.createStatement(); Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery( ResultSet rs = stmt.executeQuery(
"SELECT id, iv, type, sender, recipient, data, ack_data, sent, received, initial_hash, status, ttl, retries, next_try " + "SELECT id, iv, type, sender, recipient, data, ack_data, sent, received, initial_hash, status, ttl, retries, next_try " +
"FROM Message WHERE " + where) "FROM Message WHERE " + where)
) { ) {
while (rs.next()) { while (rs.next()) {
byte[] iv = rs.getBytes("iv"); byte[] iv = rs.getBytes("iv");
@ -120,7 +120,7 @@ public class JdbcMessageRepository extends AbstractMessageRepository implements
builder.retries(rs.getInt("retries")); builder.retries(rs.getInt("retries"));
builder.nextTry(rs.getLong("next_try")); builder.nextTry(rs.getLong("next_try"));
builder.labels(findLabels(connection, builder.labels(findLabels(connection,
"id IN (SELECT label_id FROM Message_Label WHERE message_id=" + id + ") ORDER BY ord")); "id IN (SELECT label_id FROM Message_Label WHERE message_id=" + id + ") ORDER BY ord"));
Plaintext message = builder.build(); Plaintext message = builder.build();
message.setInitialHash(rs.getBytes("initial_hash")); message.setInitialHash(rs.getBytes("initial_hash"));
result.add(message); result.add(message);
@ -134,8 +134,8 @@ public class JdbcMessageRepository extends AbstractMessageRepository implements
private List<Label> findLabels(Connection connection, String where) { private List<Label> findLabels(Connection connection, String where) {
List<Label> result = new ArrayList<>(); List<Label> result = new ArrayList<>();
try ( try (
Statement stmt = connection.createStatement(); Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, label, type, color FROM Label WHERE " + where) ResultSet rs = stmt.executeQuery("SELECT id, label, type, color FROM Label WHERE " + where)
) { ) {
while (rs.next()) { while (rs.next()) {
result.add(getLabel(rs)); result.add(getLabel(rs));
@ -148,7 +148,8 @@ public class JdbcMessageRepository extends AbstractMessageRepository implements
@Override @Override
public void save(Plaintext message) { public void save(Plaintext message) {
safeSenderIfNecessary(message); saveContactIfNecessary(message.getFrom());
saveContactIfNecessary(message.getTo());
try (Connection connection = config.getConnection()) { try (Connection connection = config.getConnection()) {
try { try {
@ -180,7 +181,7 @@ public class JdbcMessageRepository extends AbstractMessageRepository implements
} }
// save new labels // save new labels
try (PreparedStatement ps = connection.prepareStatement("INSERT INTO Message_Label VALUES (" + try (PreparedStatement ps = connection.prepareStatement("INSERT INTO Message_Label VALUES (" +
message.getId() + ", ?)")) { message.getId() + ", ?)")) {
for (Label label : message.getLabels()) { for (Label label : message.getLabels()) {
ps.setLong(1, (Long) label.getId()); ps.setLong(1, (Long) label.getId());
ps.executeUpdate(); ps.executeUpdate();
@ -190,10 +191,10 @@ public class JdbcMessageRepository extends AbstractMessageRepository implements
private void insert(Connection connection, Plaintext message) throws SQLException, IOException { private void insert(Connection connection, Plaintext message) throws SQLException, IOException {
try (PreparedStatement ps = connection.prepareStatement( try (PreparedStatement ps = connection.prepareStatement(
"INSERT INTO Message (iv, type, sender, recipient, data, ack_data, sent, received, " + "INSERT INTO Message (iv, type, sender, recipient, data, ack_data, sent, received, " +
"status, initial_hash, ttl, retries, next_try) " + "status, initial_hash, ttl, retries, next_try) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
Statement.RETURN_GENERATED_KEYS) Statement.RETURN_GENERATED_KEYS)
) { ) {
ps.setBytes(1, message.getInventoryVector() == null ? null : message.getInventoryVector().getHash()); ps.setBytes(1, message.getInventoryVector() == null ? null : message.getInventoryVector().getHash());
ps.setString(2, message.getType().name()); ps.setString(2, message.getType().name());
@ -220,9 +221,9 @@ public class JdbcMessageRepository extends AbstractMessageRepository implements
private void update(Connection connection, Plaintext message) throws SQLException, IOException { private void update(Connection connection, Plaintext message) throws SQLException, IOException {
try (PreparedStatement ps = connection.prepareStatement( try (PreparedStatement ps = connection.prepareStatement(
"UPDATE Message SET iv=?, type=?, sender=?, recipient=?, data=?, ack_data=?, sent=?, received=?, " + "UPDATE Message SET iv=?, type=?, sender=?, recipient=?, data=?, ack_data=?, sent=?, received=?, " +
"status=?, initial_hash=?, ttl=?, retries=?, next_try=? " + "status=?, initial_hash=?, ttl=?, retries=?, next_try=? " +
"WHERE id=?")) { "WHERE id=?")) {
ps.setBytes(1, message.getInventoryVector() == null ? null : message.getInventoryVector().getHash()); ps.setBytes(1, message.getInventoryVector() == null ? null : message.getInventoryVector().getHash());
ps.setString(2, message.getType().name()); ps.setString(2, message.getType().name());
ps.setString(3, message.getFrom().getAddress()); ps.setString(3, message.getFrom().getAddress());

View File

@ -109,6 +109,11 @@ public class JdbcNodeRegistry extends JdbcHelper implements NodeRegistry {
result.add(Collections.selectRandom(nodes)); result.add(Collections.selectRandom(nodes));
} }
} }
if (result.isEmpty()) {
// There might have been an error resolving domain names due to a missing internet exception.
// Try to load the stable nodes again next time.
stableNodes = null;
}
} }
return result; return result;
} }