From 14849a82ea67136190f6ce1699596fbc328f94fc Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Fri, 20 May 2016 23:58:08 +0200 Subject: [PATCH] Refactored JdbcMessageRepository so that alternative implementations can be done easier --- .gitignore | 1 + .../ports/AbstractMessageRepository.java | 123 +++++++++++++++++ .../dissem/bitmessage/utils/SqlStrings.java | 59 ++++++++ .../bitmessage/utils/SqlStringsTest.java | 8 +- .../bitmessage/repository/JdbcHelper.java | 46 +------ .../bitmessage/repository/JdbcInventory.java | 1 + .../repository/JdbcMessageRepository.java | 127 +++--------------- 7 files changed, 206 insertions(+), 159 deletions(-) create mode 100644 core/src/main/java/ch/dissem/bitmessage/ports/AbstractMessageRepository.java create mode 100644 core/src/main/java/ch/dissem/bitmessage/utils/SqlStrings.java rename repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcHelperTest.java => core/src/test/java/ch/dissem/bitmessage/utils/SqlStringsTest.java (81%) diff --git a/.gitignore b/.gitignore index fd29962..98d31cd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ ### Gradle ### .gradle build/ +classes/ # Ignore Gradle GUI config gradle-app.setting diff --git a/core/src/main/java/ch/dissem/bitmessage/ports/AbstractMessageRepository.java b/core/src/main/java/ch/dissem/bitmessage/ports/AbstractMessageRepository.java new file mode 100644 index 0000000..e037c4f --- /dev/null +++ b/core/src/main/java/ch/dissem/bitmessage/ports/AbstractMessageRepository.java @@ -0,0 +1,123 @@ +/* + * Copyright 2016 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.ports; + +import ch.dissem.bitmessage.InternalContext; +import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.entity.Plaintext; +import ch.dissem.bitmessage.entity.valueobject.Label; +import ch.dissem.bitmessage.exception.ApplicationException; +import ch.dissem.bitmessage.utils.Strings; +import ch.dissem.bitmessage.utils.UnixTime; + +import java.util.Collection; +import java.util.List; + +import static ch.dissem.bitmessage.utils.SqlStrings.join; + +public abstract class AbstractMessageRepository implements MessageRepository, InternalContext.ContextHolder { + protected InternalContext ctx; + + @Override + public void setContext(InternalContext context) { + this.ctx = context; + } + + protected void safeSenderIfNecessary(Plaintext message) { + if (message.getId() == null) { + BitmessageAddress savedAddress = ctx.getAddressRepository().getAddress(message.getFrom().getAddress()); + if (savedAddress == null) { + ctx.getAddressRepository().save(message.getFrom()); + } else if (savedAddress.getPubkey() == null && message.getFrom().getPubkey() != null) { + savedAddress.setPubkey(message.getFrom().getPubkey()); + ctx.getAddressRepository().save(savedAddress); + } + } + } + + @Override + public Plaintext getMessage(Object id) { + if (id instanceof Long) { + return single(find("id=" + id)); + } else { + throw new IllegalArgumentException("Long expected for ID"); + } + } + + @Override + public Plaintext getMessage(byte[] initialHash) { + return single(find("initial_hash=X'" + Strings.hex(initialHash) + "'")); + } + + @Override + public Plaintext getMessageForAck(byte[] ackData) { + return single(find("ack_data=X'" + Strings.hex(ackData) + "' AND status='" + Plaintext.Status.SENT + "'")); + } + + @Override + public List findMessages(Label label) { + return find("id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() + ")"); + } + + @Override + public List<Plaintext> findMessages(Plaintext.Status status, BitmessageAddress recipient) { + return find("status='" + status.name() + "' AND recipient='" + recipient.getAddress() + "'"); + } + + @Override + public List<Plaintext> findMessages(Plaintext.Status status) { + return find("status='" + status.name() + "'"); + } + + @Override + public List<Plaintext> findMessages(BitmessageAddress sender) { + return find("sender='" + sender.getAddress() + "'"); + } + + @Override + public List<Plaintext> findMessagesToResend() { + return find("status='" + Plaintext.Status.SENT.name() + "'" + + " AND next_try < " + UnixTime.now()); + } + + @Override + public List<Label> getLabels() { + return findLabels("1=1"); + } + + @Override + public List<Label> getLabels(Label.Type... types) { + return findLabels("type IN (" + join(types) + ")"); + } + + protected abstract List<Label> findLabels(String where); + + + protected <T> T single(Collection<T> collection) { + switch (collection.size()) { + case 0: + return null; + case 1: + return collection.iterator().next(); + default: + throw new ApplicationException("This shouldn't happen, found " + collection.size() + + " items, one or none was expected"); + } + } + + protected abstract List<Plaintext> find(String where); +} diff --git a/core/src/main/java/ch/dissem/bitmessage/utils/SqlStrings.java b/core/src/main/java/ch/dissem/bitmessage/utils/SqlStrings.java new file mode 100644 index 0000000..82d06e3 --- /dev/null +++ b/core/src/main/java/ch/dissem/bitmessage/utils/SqlStrings.java @@ -0,0 +1,59 @@ +/* + * Copyright 2016 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.utils; + +import ch.dissem.bitmessage.entity.payload.ObjectType; + +import static ch.dissem.bitmessage.utils.Strings.hex; + +public class SqlStrings { + 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; + } +} diff --git a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcHelperTest.java b/core/src/test/java/ch/dissem/bitmessage/utils/SqlStringsTest.java similarity index 81% rename from repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcHelperTest.java rename to core/src/test/java/ch/dissem/bitmessage/utils/SqlStringsTest.java index ee3a580..2aa1113 100644 --- a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcHelperTest.java +++ b/core/src/test/java/ch/dissem/bitmessage/utils/SqlStringsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Christian Basler + * Copyright 2016 Christian Basler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +14,16 @@ * limitations under the License. */ -package ch.dissem.bitmessage.repository; +package ch.dissem.bitmessage.utils; import org.junit.Test; import static org.junit.Assert.assertEquals; -public class JdbcHelperTest { +public class SqlStringsTest { @Test public void ensureJoinWorksWithLongArray() { long[] test = {1L, 2L}; - assertEquals("1, 2", JdbcHelper.join(test).toString()); + assertEquals("1, 2", SqlStrings.join(test).toString()); } } 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 beb4c80..b0be9c1 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java @@ -42,43 +42,7 @@ public abstract class JdbcHelper { this.config = config; } - 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 { + public static void writeBlob(PreparedStatement ps, int parameterIndex, Streamable data) throws SQLException, IOException { if (data == null) { ps.setBytes(parameterIndex, null); } else { @@ -87,12 +51,4 @@ public abstract class JdbcHelper { ps.setBytes(parameterIndex, os.toByteArray()); } } - - protected <T> T single(Collection<T> collection) { - if (collection.size() > 1) { - throw new ApplicationException("This shouldn't happen, found " + collection.size() + - " messages, one or none was expected"); - } - return collection.stream().findAny().orElse(null); - } } 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 0cc9f3b..13d3725 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import static ch.dissem.bitmessage.utils.SqlStrings.join; import static ch.dissem.bitmessage.utils.UnixTime.MINUTE; import static ch.dissem.bitmessage.utils.UnixTime.now; 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 7ac42f9..403754a 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java @@ -16,16 +16,12 @@ package ch.dissem.bitmessage.repository; -import ch.dissem.bitmessage.InternalContext; -import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.exception.ApplicationException; +import ch.dissem.bitmessage.ports.AbstractMessageRepository; import ch.dissem.bitmessage.ports.MessageRepository; -import ch.dissem.bitmessage.utils.Strings; -import ch.dissem.bitmessage.utils.TTL; -import ch.dissem.bitmessage.utils.UnixTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,34 +29,30 @@ import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.ArrayList; -import java.util.Collection; import java.util.LinkedList; import java.util.List; -public class JdbcMessageRepository extends JdbcHelper implements MessageRepository, InternalContext.ContextHolder { +import static ch.dissem.bitmessage.repository.JdbcHelper.writeBlob; + +public class JdbcMessageRepository extends AbstractMessageRepository implements MessageRepository { private static final Logger LOG = LoggerFactory.getLogger(JdbcMessageRepository.class); - private InternalContext ctx; + private final JdbcConfig config; public JdbcMessageRepository(JdbcConfig config) { - super(config); + this.config = config; } @Override - public List<Label> getLabels() { - List<Label> result = new LinkedList<>(); + protected List<Label> findLabels(String where) { try ( - Connection connection = config.getConnection(); - Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT id, label, type, color FROM Label ORDER BY ord") + Connection connection = config.getConnection() ) { - while (rs.next()) { - result.add(getLabel(rs)); - } + return findLabels(connection, where); } catch (SQLException e) { - throw new ApplicationException(e); + LOG.error(e.getMessage(), e); } - return result; + return new ArrayList<>(); } private Label getLabel(ResultSet rs) throws SQLException { @@ -75,24 +67,6 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito return label; } - @Override - public List<Label> getLabels(Label.Type... types) { - List<Label> result = new LinkedList<>(); - try ( - Connection connection = config.getConnection(); - Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT id, label, type, color FROM Label WHERE type IN (" + join(types) + - ") ORDER BY ord") - ) { - while (rs.next()) { - result.add(getLabel(rs)); - } - } catch (SQLException e) { - LOG.error(e.getMessage(), e); - } - return result; - } - @Override public int countUnread(Label label) { String where; @@ -120,60 +94,7 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito } @Override - public Plaintext getMessage(Object id) { - if (id instanceof Long) { - List<Plaintext> plaintexts = find("id=" + id); - switch (plaintexts.size()) { - case 0: - return null; - case 1: - return plaintexts.get(0); - default: - throw new ApplicationException("This shouldn't happen, found " + plaintexts.size() + - " messages, one or none was expected"); - } - } else { - throw new IllegalArgumentException("Long expected for ID"); - } - } - - @Override - public Plaintext getMessage(byte[] initialHash) { - return single(find("initial_hash=X'" + Strings.hex(initialHash) + "'")); - } - - @Override - public Plaintext getMessageForAck(byte[] ackData) { - return single(find("ack_data=X'" + Strings.hex(ackData) + "' AND status='" + Plaintext.Status.SENT + "'")); - } - - @Override - public List<Plaintext> findMessages(Label label) { - return find("id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() + ")"); - } - - @Override - public List<Plaintext> findMessages(Plaintext.Status status, BitmessageAddress recipient) { - return find("status='" + status.name() + "' AND recipient='" + recipient.getAddress() + "'"); - } - - @Override - public List<Plaintext> findMessages(Plaintext.Status status) { - return find("status='" + status.name() + "'"); - } - - @Override - public List<Plaintext> findMessages(BitmessageAddress sender) { - return find("sender='" + sender.getAddress() + "'"); - } - - @Override - public List<Plaintext> findMessagesToResend() { - return find("status='" + Plaintext.Status.SENT.name() + "'" + - " AND next_try < " + UnixTime.now()); - } - - private List<Plaintext> find(String where) { + protected List<Plaintext> find(String where) { List<Plaintext> result = new LinkedList<>(); try ( Connection connection = config.getConnection(); @@ -199,7 +120,8 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito builder.ttl(rs.getLong("ttl")); builder.retries(rs.getInt("retries")); builder.nextTry(rs.getLong("next_try")); - builder.labels(findLabels(connection, id)); + builder.labels(findLabels(connection, + "WHERE id IN (SELECT label_id FROM Message_Label WHERE message_id=" + id + ") ORDER BY ord")); Plaintext message = builder.build(); message.setInitialHash(rs.getBytes("initial_hash")); result.add(message); @@ -210,12 +132,11 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito return result; } - private Collection<Label> findLabels(Connection connection, long messageId) { + private List<Label> findLabels(Connection connection, String where) { List<Label> result = new ArrayList<>(); try ( Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT id, label, type, color FROM Label " + - "WHERE id IN (SELECT label_id FROM Message_Label WHERE message_id=" + messageId + ")") + ResultSet rs = stmt.executeQuery("SELECT id, label, type, color FROM Label WHERE " + where) ) { while (rs.next()) { result.add(getLabel(rs)); @@ -228,16 +149,7 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito @Override public void save(Plaintext message) { - // save from address if necessary - if (message.getId() == null) { - BitmessageAddress savedAddress = ctx.getAddressRepository().getAddress(message.getFrom().getAddress()); - if (savedAddress == null) { - ctx.getAddressRepository().save(message.getFrom()); - } else if (savedAddress.getPubkey() == null && message.getFrom().getPubkey() != null) { - savedAddress.setPubkey(message.getFrom().getPubkey()); - ctx.getAddressRepository().save(savedAddress); - } - } + safeSenderIfNecessary(message); try (Connection connection = config.getConnection()) { try { @@ -350,9 +262,4 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito LOG.error(e.getMessage(), e); } } - - @Override - public void setContext(InternalContext context) { - this.ctx = context; - } }