From f76864eebd8060a0d174d63079324b1f6efa2eca Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Fri, 5 Jun 2015 13:43:56 +0200 Subject: [PATCH] Added tests for all repositories, fixed some bugs and made database configurable --- .../dissem/bitmessage/demo/Application.java | 32 +++- .../dissem/bitmessage/BitmessageContext.java | 1 - .../dissem/bitmessage/entity/Plaintext.java | 1 + .../AddressFormatException.java | 2 +- .../exception/DecryptionFailedException.java | 16 ++ .../InsufficientProofOfWorkException.java | 16 ++ .../bitmessage/utils/AccessCounter.java | 18 +- .../ch/dissem/bitmessage/utils/Base58.java | 6 +- .../ch/dissem/bitmessage/utils/Bytes.java | 37 +++-- .../ch/dissem/bitmessage/utils/Strings.java | 36 ---- .../ch/dissem/bitmessage/utils/UnixTime.java | 8 +- .../bitmessage/entity/SerializationTest.java | 1 - .../dissem/bitmessage/utils/StringsTest.java | 6 - .../repository/JdbcAddressRepository.java | 40 +++-- .../bitmessage/repository/JdbcConfig.java | 55 +++++++ .../bitmessage/repository/JdbcHelper.java | 56 +++++-- .../bitmessage/repository/JdbcInventory.java | 32 ++-- .../repository/JdbcMessageRepository.java | 93 ++++++----- .../repository/JdbcNodeRegistry.java | 32 ++-- .../repository/JdbcAddressRepositoryTest.java | 126 ++++++++++++++ .../bitmessage/repository/JdbcHelperTest.java | 29 ++++ .../repository/JdbcInventoryTest.java | 132 +++++++++++++++ .../repository/JdbcMessageRepositoryTest.java | 155 ++++++++++++++++++ .../repository/JdbcNodeRegistryTest.java | 83 ++++++++++ .../bitmessage/repository/TestJdbcConfig.java | 31 ++++ 25 files changed, 860 insertions(+), 184 deletions(-) rename domain/src/main/java/ch/dissem/bitmessage/{utils => exception}/AddressFormatException.java (95%) create mode 100644 repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcConfig.java create mode 100644 repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcAddressRepositoryTest.java create mode 100644 repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcHelperTest.java create mode 100644 repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcInventoryTest.java create mode 100644 repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcMessageRepositoryTest.java create mode 100644 repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcNodeRegistryTest.java create mode 100644 repositories/src/test/java/ch/dissem/bitmessage/repository/TestJdbcConfig.java diff --git a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java index 3faa77f..4489e6c 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java @@ -1,3 +1,19 @@ +/* + * Copyright 2015 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package ch.dissem.bitmessage.demo; import ch.dissem.bitmessage.BitmessageContext; @@ -5,10 +21,7 @@ import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.networking.NetworkNode; -import ch.dissem.bitmessage.repository.JdbcAddressRepository; -import ch.dissem.bitmessage.repository.JdbcInventory; -import ch.dissem.bitmessage.repository.JdbcMessageRepository; -import ch.dissem.bitmessage.repository.JdbcNodeRegistry; +import ch.dissem.bitmessage.repository.*; import ch.dissem.bitmessage.utils.Strings; import org.flywaydb.core.internal.util.logging.slf4j.Slf4jLog; import org.flywaydb.core.internal.util.logging.slf4j.Slf4jLogCreator; @@ -29,12 +42,13 @@ public class Application { private BitmessageContext ctx; public Application() { + JdbcConfig jdbcConfig = new JdbcConfig(); ctx = new BitmessageContext.Builder() - .addressRepo(new JdbcAddressRepository()) - .inventory(new JdbcInventory()) - .nodeRegistry(new JdbcNodeRegistry()) + .addressRepo(new JdbcAddressRepository(jdbcConfig)) + .inventory(new JdbcInventory(jdbcConfig)) + .nodeRegistry(new JdbcNodeRegistry(jdbcConfig)) + .messageRepo(new JdbcMessageRepository(jdbcConfig)) .networkHandler(new NetworkNode()) - .messageRepo(new JdbcMessageRepository()) .port(48444) .streams(1) .build(); @@ -59,7 +73,7 @@ public class Application { System.out.println("i) identities"); System.out.println("c) contacts"); System.out.println("m) messages"); - System.out.println("e) Exit"); + System.out.println("e) exit"); command = nextCommand(); try { diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index 3ced983..b3bae99 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -97,7 +97,6 @@ public class BitmessageContext { Plaintext msg = new Plaintext.Builder() .from(from) .to(to) - .encoding(Encoding.SIMPLE) .message(subject, message) .build(); if (to.getPubkey() == null) { diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java b/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java index 8027531..340a774 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java @@ -340,6 +340,7 @@ public class Plaintext implements Streamable { public Builder message(String subject, String message) { try { + this.encoding = Encoding.SIMPLE.getCode(); this.message = ("Subject:" + subject + '\n' + "Body:" + message).getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/AddressFormatException.java b/domain/src/main/java/ch/dissem/bitmessage/exception/AddressFormatException.java similarity index 95% rename from domain/src/main/java/ch/dissem/bitmessage/utils/AddressFormatException.java rename to domain/src/main/java/ch/dissem/bitmessage/exception/AddressFormatException.java index 2120a9e..7da990a 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/AddressFormatException.java +++ b/domain/src/main/java/ch/dissem/bitmessage/exception/AddressFormatException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package ch.dissem.bitmessage.utils; +package ch.dissem.bitmessage.exception; /** * Indicates an illegal Bitmessage address diff --git a/domain/src/main/java/ch/dissem/bitmessage/exception/DecryptionFailedException.java b/domain/src/main/java/ch/dissem/bitmessage/exception/DecryptionFailedException.java index 05417da..c68a050 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/exception/DecryptionFailedException.java +++ b/domain/src/main/java/ch/dissem/bitmessage/exception/DecryptionFailedException.java @@ -1,3 +1,19 @@ +/* + * Copyright 2015 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package ch.dissem.bitmessage.exception; public class DecryptionFailedException extends Exception { diff --git a/domain/src/main/java/ch/dissem/bitmessage/exception/InsufficientProofOfWorkException.java b/domain/src/main/java/ch/dissem/bitmessage/exception/InsufficientProofOfWorkException.java index 069bd79..685e710 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/exception/InsufficientProofOfWorkException.java +++ b/domain/src/main/java/ch/dissem/bitmessage/exception/InsufficientProofOfWorkException.java @@ -1,3 +1,19 @@ +/* + * Copyright 2015 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package ch.dissem.bitmessage.exception; import ch.dissem.bitmessage.utils.Strings; diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/AccessCounter.java b/domain/src/main/java/ch/dissem/bitmessage/utils/AccessCounter.java index 5143fc8..55c23c3 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/AccessCounter.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/AccessCounter.java @@ -17,23 +17,35 @@ package ch.dissem.bitmessage.utils; /** - * Created by chris on 13.04.15. + * Intended to count the bytes read or written during (de-)serialization. */ public class AccessCounter { private int count; + /** + * Increases the counter by one, if not null. + */ public static void inc(AccessCounter counter) { if (counter != null) counter.inc(); } - public static void inc(AccessCounter counter, int count) { - if (counter != null) counter.inc(count); + /** + * Increases the counter by length, if not null. + */ + public static void inc(AccessCounter counter, int length) { + if (counter != null) counter.inc(length); } + /** + * Increases the counter by one. + */ private void inc() { count++; } + /** + * Increases the counter by length. + */ private void inc(int length) { count += length; } diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Base58.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Base58.java index c76a4e1..26ed801 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Base58.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Base58.java @@ -17,12 +17,16 @@ package ch.dissem.bitmessage.utils; +import ch.dissem.bitmessage.exception.AddressFormatException; + import java.io.UnsupportedEncodingException; import static java.util.Arrays.copyOfRange; /** - * Base58 encoder and decoder + * Base58 encoder and decoder. + * + * @author Christian Basler: I removed some dependencies to the BitcoinJ code so it can be used here more easily. */ public class Base58 { private static char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java index cedc1c1..411f062 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java @@ -24,6 +24,9 @@ import java.util.Arrays; /** * A helper class for working with byte arrays interpreted as unsigned big endian integers. + * This is one part due to the fact that Java doesn't support unsigned numbers, and another + * part so we don't have to convert between byte arrays and numbers in time critical + * situations. */ public class Bytes { public static void inc(byte[] nonce) { @@ -33,6 +36,9 @@ public class Bytes { } } + /** + * Increases nonce by value, which is used as an unsigned byte value. + */ public static void inc(byte[] nonce, byte value) { int i = nonce.length - 1; nonce[i] += value; @@ -67,7 +73,7 @@ public class Bytes { } /** - * Returns true if a < b, where the first [size] bytes are checked. + * Returns true if a < b, where the first [size] bytes are used as the numbers to check. */ public static boolean lt(byte[] a, byte[] b, int size) { for (int i = 0; i < size; i++) { @@ -84,6 +90,9 @@ public class Bytes { return a < b; } + /** + * Returns a new byte array of length, left-padded with '0'. + */ public static byte[] expand(byte[] source, int size) { byte[] result = new byte[size]; System.arraycopy(source, 0, result, size - source.length, source.length); @@ -99,19 +108,10 @@ public class Bytes { return result; } - public static byte[] subArray(byte[] source, int offset, int length) { - byte[] result = new byte[length]; - System.arraycopy(source, offset, result, 0, length); - return result; - } - - public static byte[] concatenate(byte first, byte[] bytes) { - byte[] result = new byte[bytes.length + 1]; - result[0] = first; - System.arraycopy(bytes, 0, result, 1, bytes.length); - return result; - } - + /** + * Returns the byte array that hex represents. This is meant for test use and should be rewritten if used in + * production code. + */ public static byte[] fromHex(String hex) { if (hex.length() % 2 != 0) throw new IllegalArgumentException("expected even number of characters"); byte[] result = new byte[hex.length() / 2]; @@ -135,6 +135,9 @@ public class Bytes { throw new IllegalArgumentException("'" + c + "' is not a valid hex value"); } + /** + * Returns the number of leading '0' of a byte array. + */ public static int numberOfLeadingZeros(byte[] bytes) { int i; for (i = 0; i < bytes.length; i++) { @@ -143,10 +146,16 @@ public class Bytes { return i; } + /** + * Returns a copy of bytes with leading zeroes stripped. + */ public static byte[] stripLeadingZeros(byte[] bytes) { return Arrays.copyOfRange(bytes, numberOfLeadingZeros(bytes), bytes.length); } + /** + * Returns the byte array of the serialized data. + */ public static byte[] from(Streamable data) { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Strings.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Strings.java index 0d2788c..5c1aae9 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Strings.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Strings.java @@ -23,42 +23,6 @@ import ch.dissem.bitmessage.entity.payload.ObjectType; * TODO: Probably this should be split in a GUI related and an SQL related utility class. */ public class Strings { - public static StringBuilder join(byte[]... objects) { - StringBuilder streamList = new StringBuilder(); - for (int i = 0; i < objects.length; i++) { - if (i > 0) streamList.append(", "); - streamList.append(hex(objects[i])); - } - return streamList; - } - - public static StringBuilder join(long... objects) { - StringBuilder streamList = new StringBuilder(); - for (int i = 0; i < objects.length; i++) { - if (i > 0) streamList.append(", "); - streamList.append(objects[i]); - } - return streamList; - } - - public static StringBuilder join(ObjectType... types) { - StringBuilder streamList = new StringBuilder(); - for (int i = 0; i < types.length; i++) { - if (i > 0) streamList.append(", "); - streamList.append(types[i].getNumber()); - } - return streamList; - } - - public static StringBuilder join(Enum... types) { - StringBuilder streamList = new StringBuilder(); - for (int i = 0; i < types.length; i++) { - if (i > 0) streamList.append(", "); - streamList.append('\'').append(types[i].name()).append('\''); - } - return streamList; - } - public static StringBuilder join(Object... objects) { StringBuilder streamList = new StringBuilder(); for (int i = 0; i < objects.length; i++) { diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/UnixTime.java b/domain/src/main/java/ch/dissem/bitmessage/utils/UnixTime.java index d57c637..cb21701 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/UnixTime.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/UnixTime.java @@ -17,9 +17,12 @@ package ch.dissem.bitmessage.utils; /** - * Created by chris on 18.04.15. + * A simple utility class that simplifies using the second based time used in Bitmessage. */ public class UnixTime { + /** + * Length of a day in seconds, intended for use with {@link #now(long)}. + */ public static final long DAY = 60 * 60 * 24; /** @@ -29,6 +32,9 @@ public class UnixTime { return System.currentTimeMillis() / 1000; } + /** + * Same as {@link #now()} + shiftSeconds, but might be more readable. + */ public static long now(long shiftSeconds) { return (System.currentTimeMillis() / 1000) + shiftSeconds; } diff --git a/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java b/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java index 9817271..406a7d6 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java @@ -81,7 +81,6 @@ public class SerializationTest { Plaintext p1 = new Plaintext.Builder() .from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) .to(TestUtils.loadContact()) - .encoding(Plaintext.Encoding.SIMPLE) .message("Subject", "Message") .ack("ack".getBytes()) .signature(new byte[0]) diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/StringsTest.java b/domain/src/test/java/ch/dissem/bitmessage/utils/StringsTest.java index c154b5e..ef54165 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/utils/StringsTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/utils/StringsTest.java @@ -21,12 +21,6 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; public class StringsTest { - @Test - public void ensureJoinWorksWithLongArray() { - long[] test = {1L, 2L}; - assertEquals("1, 2", Strings.join(test).toString()); - } - @Test public void testHexString() { assertEquals("48656c6c6f21", Strings.hex("Hello!".getBytes()).toString()); diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java index 413d468..372f83b 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java @@ -34,12 +34,13 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; -/** - * Created by chris on 23.04.15. - */ public class JdbcAddressRepository extends JdbcHelper implements AddressRepository { private static final Logger LOG = LoggerFactory.getLogger(JdbcAddressRepository.class); + public JdbcAddressRepository(JdbcConfig config) { + super(config); + } + @Override public BitmessageAddress findContact(byte[] ripeOrTag) { for (BitmessageAddress address : find("public_key is null")) { @@ -81,8 +82,8 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito private List find(String where) { List result = new LinkedList<>(); - try { - Statement stmt = getConnection().createStatement(); + try (Connection connection = config.getConnection()) { + Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT address, alias, public_key, private_key, subscribed FROM Address WHERE " + where); while (rs.next()) { BitmessageAddress address; @@ -114,8 +115,8 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito } private boolean exists(BitmessageAddress address) { - try { - Statement stmt = getConnection().createStatement(); + try (Connection connection = config.getConnection()) { + Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM Address WHERE address='" + address.getAddress() + "'"); rs.next(); return rs.getInt(1) > 0; @@ -139,23 +140,26 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito } private void update(BitmessageAddress address) throws IOException, SQLException { - PreparedStatement ps = getConnection().prepareStatement( + try(Connection connection = config.getConnection()){ + PreparedStatement ps = connection.prepareStatement( "UPDATE Address SET alias=?, public_key=?, private_key=? WHERE address=?"); ps.setString(1, address.getAlias()); writePubkey(ps, 2, address.getPubkey()); writeBlob(ps, 3, address.getPrivateKey()); ps.setString(4, address.getAddress()); ps.executeUpdate(); - } + }} private void insert(BitmessageAddress address) throws IOException, SQLException { - PreparedStatement ps = getConnection().prepareStatement( - "INSERT INTO Address (address, alias, public_key, private_key) VALUES (?, ?, ?, ?)"); - ps.setString(1, address.getAddress()); - ps.setString(2, address.getAlias()); - writePubkey(ps, 3, address.getPubkey()); - writeBlob(ps, 4, address.getPrivateKey()); - ps.executeUpdate(); + try (Connection connection = config.getConnection()) { + PreparedStatement ps = connection.prepareStatement( + "INSERT INTO Address (address, alias, public_key, private_key) VALUES (?, ?, ?, ?)"); + ps.setString(1, address.getAddress()); + ps.setString(2, address.getAlias()); + writePubkey(ps, 3, address.getPubkey()); + writeBlob(ps, 4, address.getPrivateKey()); + ps.executeUpdate(); + } } protected void writePubkey(PreparedStatement ps, int parameterIndex, Pubkey data) throws SQLException, IOException { @@ -171,8 +175,8 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito @Override public void remove(BitmessageAddress address) { - try { - Statement stmt = getConnection().createStatement(); + try (Connection connection = config.getConnection()) { + Statement stmt = connection.createStatement(); stmt.executeUpdate("DELETE FROM Address WHERE address = '" + address.getAddress() + "'"); } catch (SQLException e) { LOG.error(e.getMessage(), e); diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcConfig.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcConfig.java new file mode 100644 index 0000000..a379708 --- /dev/null +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcConfig.java @@ -0,0 +1,55 @@ +/* + * Copyright 2015 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.dissem.bitmessage.repository; + +import org.flywaydb.core.Flyway; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +/** + * The base configuration for all JDBC based repositories. You should only make one instance, + * as flyway initializes/updates the database at object creation. + */ +public class JdbcConfig { + protected final Flyway flyway; + private final String dbUrl; + private final String dbUser; + private final String dbPassword; + + public JdbcConfig(String dbUrl, String dbUser, String dbPassword) { + this.dbUrl = dbUrl; + this.dbUser = dbUser; + this.dbPassword = dbPassword; + this.flyway = new Flyway(); + flyway.setDataSource(dbUrl, dbUser, dbPassword); + flyway.migrate(); + } + + public JdbcConfig() { + this("jdbc:h2:~/jabit", "sa", null); + } + + public Connection getConnection() { + try { + return DriverManager.getConnection(dbUrl, dbUser, dbPassword); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java index 732f276..8795388 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java @@ -17,6 +17,7 @@ package ch.dissem.bitmessage.repository; import ch.dissem.bitmessage.entity.Streamable; +import ch.dissem.bitmessage.entity.payload.ObjectType; import org.flywaydb.core.Flyway; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,21 +27,54 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.sql.*; +import static ch.dissem.bitmessage.utils.Strings.hex; + /** * Helper class that does Flyway migration, provides JDBC connections and some helper methods. */ abstract class JdbcHelper { private static final Logger LOG = LoggerFactory.getLogger(JdbcHelper.class); - private static final String DB_URL = "jdbc:h2:~/jabit"; - private static final String DB_USER = "sa"; - private static final String DB_PWD = null; + protected final JdbcConfig config; + protected JdbcHelper(JdbcConfig config) { + this.config = config; + } - static { - Flyway flyway = new Flyway(); - flyway.setDataSource(DB_URL, DB_USER, DB_PWD); - flyway.migrate(); + public static StringBuilder join(long... objects) { + StringBuilder streamList = new StringBuilder(); + for (int i = 0; i < objects.length; i++) { + if (i > 0) streamList.append(", "); + streamList.append(objects[i]); + } + return streamList; + } + + public static StringBuilder join(byte[]... objects) { + StringBuilder streamList = new StringBuilder(); + for (int i = 0; i < objects.length; i++) { + if (i > 0) streamList.append(", "); + streamList.append(hex(objects[i])); + } + return streamList; + } + + public static StringBuilder join(ObjectType... types) { + StringBuilder streamList = new StringBuilder(); + for (int i = 0; i < types.length; i++) { + if (i > 0) streamList.append(", "); + streamList.append(types[i].getNumber()); + } + return streamList; + } + + public static StringBuilder join(Enum... types) { + StringBuilder streamList = new StringBuilder(); + for (int i = 0; i < types.length; i++) { + if (i > 0) streamList.append(", "); + streamList.append('\'').append(types[i].name()).append('\''); + } + return streamList; } protected void writeBlob(PreparedStatement ps, int parameterIndex, Streamable data) throws SQLException, IOException { @@ -53,12 +87,4 @@ abstract class JdbcHelper { ps.setBlob(parameterIndex, (Blob) null); } } - - protected Connection getConnection() { - try { - return DriverManager.getConnection(DB_URL, DB_USER, DB_PWD); - } catch (SQLException e) { - throw new RuntimeException(e); - } - } } diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java index 396e70d..f499d6b 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java @@ -28,20 +28,20 @@ import java.sql.*; import java.util.LinkedList; import java.util.List; -import static ch.dissem.bitmessage.utils.Strings.join; import static ch.dissem.bitmessage.utils.UnixTime.now; -/** - * Created by chris on 24.04.15. - */ public class JdbcInventory extends JdbcHelper implements Inventory { private static final Logger LOG = LoggerFactory.getLogger(JdbcInventory.class); + public JdbcInventory(JdbcConfig config) { + super(config); + } + @Override public List getInventory(long... streams) { List result = new LinkedList<>(); - try { - Statement stmt = getConnection().createStatement(); + try (Connection connection = config.getConnection()) { + Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT hash FROM Inventory WHERE expires > " + now() + " AND stream IN (" + join(streams) + ")"); while (rs.next()) { @@ -54,8 +54,8 @@ public class JdbcInventory extends JdbcHelper implements Inventory { } private List getFullInventory(long... streams) { List result = new LinkedList<>(); - try { - Statement stmt = getConnection().createStatement(); + try (Connection connection = config.getConnection()) { + Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT hash FROM Inventory WHERE stream IN (" + join(streams) + ")"); while (rs.next()) { result.add(new InventoryVector(rs.getBytes("hash"))); @@ -74,8 +74,8 @@ public class JdbcInventory extends JdbcHelper implements Inventory { @Override public ObjectMessage getObject(InventoryVector vector) { - try { - Statement stmt = getConnection().createStatement(); + try (Connection connection = config.getConnection()) { + Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT data, version FROM Inventory WHERE hash = X'" + vector + "'"); if (rs.next()) { Blob data = rs.getBlob("data"); @@ -92,7 +92,7 @@ public class JdbcInventory extends JdbcHelper implements Inventory { @Override public List getObjects(long stream, long version, ObjectType... types) { - try { + try (Connection connection = config.getConnection()) { StringBuilder query = new StringBuilder("SELECT data, version FROM Inventory WHERE 1=1"); if (stream > 0) { query.append(" AND stream = ").append(stream); @@ -103,7 +103,7 @@ public class JdbcInventory extends JdbcHelper implements Inventory { if (types.length > 0) { query.append(" AND type IN (").append(join(types)).append(")"); } - Statement stmt = getConnection().createStatement(); + Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(query.toString()); List result = new LinkedList<>(); while (rs.next()) { @@ -119,8 +119,8 @@ public class JdbcInventory extends JdbcHelper implements Inventory { @Override public void storeObject(ObjectMessage object) { - try { - PreparedStatement ps = getConnection().prepareStatement("INSERT INTO Inventory (hash, stream, expires, data, type, version) VALUES (?, ?, ?, ?, ?, ?)"); + try (Connection connection = config.getConnection()) { + PreparedStatement ps = connection.prepareStatement("INSERT INTO Inventory (hash, stream, expires, data, type, version) VALUES (?, ?, ?, ?, ?, ?)"); InventoryVector iv = object.getInventoryVector(); LOG.trace("Storing object " + iv); ps.setBytes(1, iv.getHash()); @@ -139,9 +139,9 @@ public class JdbcInventory extends JdbcHelper implements Inventory { @Override public void cleanup() { - try { + try (Connection connection = config.getConnection()) { // We delete only objects that expired 5 minutes ago or earlier, so we don't request objects we just deleted - getConnection().createStatement().executeUpdate("DELETE FROM Inventory WHERE expires < " + (now() - 300)); + connection.createStatement().executeUpdate("DELETE FROM Inventory WHERE expires < " + (now() - 300)); } catch (SQLException e) { LOG.debug(e.getMessage(), e); } diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java index 0911ad6..8d0ca58 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java @@ -21,7 +21,6 @@ import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.ports.MessageRepository; -import ch.dissem.bitmessage.utils.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,17 +36,21 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito private InternalContext ctx; + public JdbcMessageRepository(JdbcConfig config) { + super(config); + } + @Override public List