Some refactoring
- I didn't like the way the context was initialized - The DatabaseRepository got too complicated
This commit is contained in:
		| @@ -16,17 +16,17 @@ | ||||
|  | ||||
| package ch.dissem.bitmessage.demo; | ||||
|  | ||||
| import ch.dissem.bitmessage.Context; | ||||
| import ch.dissem.bitmessage.BitmessageContext; | ||||
| import ch.dissem.bitmessage.entity.payload.ObjectPayload; | ||||
| import ch.dissem.bitmessage.inventory.DatabaseRepository; | ||||
| import ch.dissem.bitmessage.inventory.JdbcAddressRepository; | ||||
| import ch.dissem.bitmessage.inventory.JdbcInventory; | ||||
| import ch.dissem.bitmessage.inventory.JdbcNodeRegistry; | ||||
| import ch.dissem.bitmessage.networking.NetworkNode; | ||||
| import ch.dissem.bitmessage.ports.NetworkHandler; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import java.io.BufferedReader; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStreamReader; | ||||
| import java.util.Scanner; | ||||
|  | ||||
| /** | ||||
| @@ -36,11 +36,15 @@ public class Main { | ||||
|     private final static Logger LOG = LoggerFactory.getLogger(Main.class); | ||||
|  | ||||
|     public static void main(String[] args) throws IOException { | ||||
|         NetworkNode networkNode = new NetworkNode(); | ||||
|         DatabaseRepository repo = new DatabaseRepository(); | ||||
|         Context.init(repo, repo, networkNode, 48444); | ||||
|         Context.getInstance().addStream(1); | ||||
|         networkNode.start(new NetworkHandler.MessageListener() { | ||||
|         BitmessageContext ctx = new BitmessageContext.Builder() | ||||
|                 .addressRepo(new JdbcAddressRepository()) | ||||
|                 .inventory(new JdbcInventory()) | ||||
|                 .nodeRegistry(new JdbcNodeRegistry()) | ||||
|                 .networkHandler(new NetworkNode()) | ||||
|                 .port(48444) | ||||
|                 .streams(1) | ||||
|                 .build(); | ||||
|         ctx.getNetworkHandler().start(new NetworkHandler.MessageListener() { | ||||
|             @Override | ||||
|             public void receive(ObjectPayload payload) { | ||||
| //                LOG.info("message received: " + payload); | ||||
| @@ -52,6 +56,6 @@ public class Main { | ||||
|         Scanner scanner = new Scanner(System.in); | ||||
|         scanner.nextLine(); | ||||
|         LOG.info("Shutting down client"); | ||||
|         networkNode.stop(); | ||||
|         ctx.getNetworkHandler().stop(); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										174
									
								
								domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| /* | ||||
|  * 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; | ||||
|  | ||||
| import ch.dissem.bitmessage.ports.AddressRepository; | ||||
| import ch.dissem.bitmessage.ports.Inventory; | ||||
| import ch.dissem.bitmessage.ports.NetworkHandler; | ||||
| import ch.dissem.bitmessage.ports.NodeRegistry; | ||||
|  | ||||
| import java.util.Collection; | ||||
| import java.util.TreeSet; | ||||
|  | ||||
| /** | ||||
|  * Created by chris on 05.04.15. | ||||
|  */ | ||||
| public class BitmessageContext { | ||||
|     public static final int CURRENT_VERSION = 3; | ||||
|  | ||||
|     private Inventory inventory; | ||||
|     private NodeRegistry nodeRegistry; | ||||
|     private NetworkHandler networkHandler; | ||||
|     private AddressRepository addressRepo; | ||||
|  | ||||
|     private Collection<Long> streams = new TreeSet<>(); | ||||
|  | ||||
|     private int port; | ||||
|  | ||||
|     private long networkNonceTrialsPerByte = 1000; | ||||
|     private long networkExtraBytes = 1000; | ||||
|  | ||||
|     private BitmessageContext(Builder builder) { | ||||
|         port = builder.port; | ||||
|         inventory = builder.inventory; | ||||
|         nodeRegistry = builder.nodeRegistry; | ||||
|         networkHandler = builder.networkHandler; | ||||
|         addressRepo = builder.addressRepo; | ||||
|         streams = builder.streams; | ||||
|  | ||||
|         init(inventory, nodeRegistry, networkHandler, addressRepo); | ||||
|     } | ||||
|  | ||||
|     private void init(Object... objects) { | ||||
|         for (Object o : objects) { | ||||
|             if (o instanceof ContextHolder) { | ||||
|                 ((ContextHolder) o).setContext(this); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public Inventory getInventory() { | ||||
|         return inventory; | ||||
|     } | ||||
|  | ||||
|     public NodeRegistry getAddressRepository() { | ||||
|         return nodeRegistry; | ||||
|     } | ||||
|  | ||||
|     public NetworkHandler getNetworkHandler() { | ||||
|         return networkHandler; | ||||
|     } | ||||
|  | ||||
|     public int getPort() { | ||||
|         return port; | ||||
|     } | ||||
|  | ||||
|     public long[] getStreams() { | ||||
|         long[] result = new long[streams.size()]; | ||||
|         int i = 0; | ||||
|         for (long stream : streams) { | ||||
|             result[i++] = stream; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public void addStream(long stream) { | ||||
|         streams.add(stream); | ||||
|     } | ||||
|  | ||||
|     public void removeStream(long stream) { | ||||
|         streams.remove(stream); | ||||
|     } | ||||
|  | ||||
|     public long getNetworkNonceTrialsPerByte() { | ||||
|         return networkNonceTrialsPerByte; | ||||
|     } | ||||
|  | ||||
|     public long getNetworkExtraBytes() { | ||||
|         return networkExtraBytes; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public interface ContextHolder { | ||||
|         void setContext(BitmessageContext context); | ||||
|     } | ||||
|  | ||||
|     public static final class Builder { | ||||
|         private int port = 8444; | ||||
|         private Inventory inventory; | ||||
|         private NodeRegistry nodeRegistry; | ||||
|         private NetworkHandler networkHandler; | ||||
|         private AddressRepository addressRepo; | ||||
|         private Collection<Long> streams; | ||||
|  | ||||
|         public Builder() { | ||||
|         } | ||||
|  | ||||
|         public Builder port(int port) { | ||||
|             this.port = port; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder inventory(Inventory inventory) { | ||||
|             this.inventory = inventory; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder nodeRegistry(NodeRegistry nodeRegistry) { | ||||
|             this.nodeRegistry = nodeRegistry; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder networkHandler(NetworkHandler networkHandler) { | ||||
|             this.networkHandler = networkHandler; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder addressRepo(AddressRepository addressRepo) { | ||||
|             this.addressRepo = addressRepo; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder streams(Collection<Long> streams) { | ||||
|             this.streams = streams; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder streams(long... streams) { | ||||
|             this.streams = new TreeSet<>(); | ||||
|             for (long stream : streams) { | ||||
|                 this.streams.add(stream); | ||||
|             } | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public BitmessageContext build() { | ||||
|             nonNull("inventory", inventory); | ||||
|             nonNull("nodeRegistry", nodeRegistry); | ||||
|             nonNull("networkHandler", networkHandler); | ||||
|             nonNull("addressRepo", addressRepo); | ||||
|             if (streams == null) { | ||||
|                 streams(1); | ||||
|             } | ||||
|             return new BitmessageContext(this); | ||||
|         } | ||||
|  | ||||
|         private void nonNull(String name, Object o) { | ||||
|             if (o == null) throw new IllegalStateException(name + " must not be null"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,97 +0,0 @@ | ||||
| /* | ||||
|  * 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; | ||||
|  | ||||
| import ch.dissem.bitmessage.ports.NodeRegistry; | ||||
| import ch.dissem.bitmessage.ports.Inventory; | ||||
| import ch.dissem.bitmessage.ports.NetworkHandler; | ||||
|  | ||||
| import java.util.Collection; | ||||
| import java.util.TreeSet; | ||||
|  | ||||
| /** | ||||
|  * Created by chris on 05.04.15. | ||||
|  */ | ||||
| public class Context { | ||||
|     public static final int CURRENT_VERSION = 3; | ||||
|  | ||||
|     private static Context instance; | ||||
|  | ||||
|     private Inventory inventory; | ||||
|     private NodeRegistry addressRepo; | ||||
|     private NetworkHandler networkHandler; | ||||
|  | ||||
|     private Collection<Long> streams = new TreeSet<>(); | ||||
|  | ||||
|     private int port; | ||||
|  | ||||
|     private long networkNonceTrialsPerByte = 1000; | ||||
|     private long networkExtraBytes = 1000; | ||||
|  | ||||
|     private Context(Inventory inventory, NodeRegistry addressRepo, | ||||
|                     NetworkHandler networkHandler, int port) { | ||||
|         this.inventory = inventory; | ||||
|         this.addressRepo = addressRepo; | ||||
|         this.networkHandler = networkHandler; | ||||
|         this.port = port; | ||||
|     } | ||||
|  | ||||
|     public static void init(Inventory inventory, NodeRegistry nodeRegistry, NetworkHandler networkHandler, int port) { | ||||
|         instance = new Context(inventory, nodeRegistry, networkHandler, port); | ||||
|     } | ||||
|  | ||||
|     public static Context getInstance() { | ||||
|         return instance; | ||||
|     } | ||||
|  | ||||
|     public Inventory getInventory() { | ||||
|         return inventory; | ||||
|     } | ||||
|  | ||||
|     public NodeRegistry getAddressRepository() { | ||||
|         return addressRepo; | ||||
|     } | ||||
|  | ||||
|     public int getPort() { | ||||
|         return port; | ||||
|     } | ||||
|  | ||||
|     public long[] getStreams() { | ||||
|         long[] result = new long[streams.size()]; | ||||
|         int i = 0; | ||||
|         for (long stream : streams) { | ||||
|             result[i++] = stream; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public void addStream(long stream) { | ||||
|         streams.add(stream); | ||||
|     } | ||||
|  | ||||
|     public void removeStream(long stream) { | ||||
|         streams.remove(stream); | ||||
|     } | ||||
|  | ||||
|     public long getNetworkNonceTrialsPerByte() { | ||||
|         return networkNonceTrialsPerByte; | ||||
|     } | ||||
|  | ||||
|     public long getNetworkExtraBytes() { | ||||
|         return networkExtraBytes; | ||||
|     } | ||||
| } | ||||
| @@ -16,7 +16,7 @@ | ||||
|  | ||||
| package ch.dissem.bitmessage.entity; | ||||
|  | ||||
| import ch.dissem.bitmessage.Context; | ||||
| import ch.dissem.bitmessage.BitmessageContext; | ||||
| import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | ||||
| import ch.dissem.bitmessage.utils.Encode; | ||||
| import ch.dissem.bitmessage.utils.UnixTime; | ||||
| @@ -147,7 +147,7 @@ public class Version implements MessagePayload { | ||||
|         } | ||||
|  | ||||
|         public Builder defaults() { | ||||
|             version = Context.CURRENT_VERSION; | ||||
|             version = BitmessageContext.CURRENT_VERSION; | ||||
|             services = 1; | ||||
|             timestamp = UnixTime.now(); | ||||
|             nonce = new Random().nextInt(); | ||||
|   | ||||
| @@ -16,6 +16,7 @@ | ||||
|  | ||||
| package ch.dissem.bitmessage.ports; | ||||
|  | ||||
| import ch.dissem.bitmessage.BitmessageContext; | ||||
| import ch.dissem.bitmessage.entity.payload.ObjectPayload; | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -133,7 +133,7 @@ public class Security { | ||||
|                                       long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { | ||||
|         byte[] publicSigningKey = EC_PARAMETERS.getG().multiply(keyToBigInt(privateSigningKey)).getEncoded(false); | ||||
|         byte[] publicEncryptionKey = EC_PARAMETERS.getG().multiply(keyToBigInt(privateEncryptionKey)).getEncoded(false); | ||||
|         return Factory.createPubkey(Bytes.subArray(publicSigningKey, 1, 64), Bytes.subArray(publicEncryptionKey, 1, 64), | ||||
|         return Factory.createPubkey(version, Bytes.subArray(publicSigningKey, 1, 64), Bytes.subArray(publicEncryptionKey, 1, 64), | ||||
|                 nonceTrialsPerByte, extraBytes, features); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,199 +0,0 @@ | ||||
| /* | ||||
|  * 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.inventory; | ||||
|  | ||||
| import ch.dissem.bitmessage.entity.ObjectMessage; | ||||
| import ch.dissem.bitmessage.entity.Streamable; | ||||
| import ch.dissem.bitmessage.entity.valueobject.InventoryVector; | ||||
| import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | ||||
| import ch.dissem.bitmessage.factory.Factory; | ||||
| import ch.dissem.bitmessage.ports.Inventory; | ||||
| import ch.dissem.bitmessage.ports.NodeRegistry; | ||||
| import org.flywaydb.core.Flyway; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import java.io.ByteArrayInputStream; | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.IOException; | ||||
| 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; | ||||
|  | ||||
| /** | ||||
|  * Stores everything in a database | ||||
|  */ | ||||
| public class DatabaseRepository implements Inventory, NodeRegistry { | ||||
|     private static final Logger LOG = LoggerFactory.getLogger(DatabaseRepository.class); | ||||
|  | ||||
|     private static final String DB_URL = "jdbc:h2:~/jabit"; | ||||
|     private static final String DB_USER = "sa"; | ||||
|     private static final String DB_PWD = null; | ||||
|  | ||||
|  | ||||
|     public DatabaseRepository() { | ||||
|         Flyway flyway = new Flyway(); | ||||
|         flyway.setDataSource(DB_URL, DB_USER, null); | ||||
|         flyway.migrate(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<NetworkAddress> getKnownAddresses(int limit, long... streams) { | ||||
|         List<NetworkAddress> result = new LinkedList<>(); | ||||
|         try { | ||||
|             Statement stmt = getConnection().createStatement(); | ||||
|             ResultSet rs = stmt.executeQuery("SELECT * FROM Node WHERE Stream IN (" + join(streams) + ")"); | ||||
|             while (rs.next()) { | ||||
| //                result.add(new NetworkAddress.Builder() | ||||
| //                        .ipv6(rs.getBytes("ip")) | ||||
| //                        .port(rs.getByte("port")) | ||||
| //                        .services(rs.getLong("services")) | ||||
| //                        .stream(rs.getLong("stream")) | ||||
| //                        .time(rs.getLong("time")) | ||||
| //                        .build()); | ||||
|             } | ||||
|         } catch (SQLException e) { | ||||
|             LOG.error(e.getMessage(), e); | ||||
|         } | ||||
|         if (result.isEmpty()) { | ||||
|             // FIXME: this is for testing purposes, remove it! | ||||
|             result.add(new NetworkAddress.Builder().ipv4(127, 0, 0, 1).port(8444).build()); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void offerAddresses(List<NetworkAddress> addresses) { | ||||
|         try { | ||||
|             Connection connection = getConnection(); | ||||
|             PreparedStatement exists = connection.prepareStatement("SELECT port FROM Node WHERE ip = ? AND port = ? AND stream = ?"); | ||||
|             PreparedStatement insert = connection.prepareStatement( | ||||
|                     "INSERT INTO Node (ip, port, services, stream, time) VALUES (?, ?, ?, ?, ?)"); | ||||
|             PreparedStatement update = connection.prepareStatement( | ||||
|                     "UPDATE Node SET services = ?, time = ? WHERE ip = ? AND port = ? AND stream = ?"); | ||||
|             for (NetworkAddress node : addresses) { | ||||
|                 exists.setBytes(1, node.getIPv6()); | ||||
|                 exists.setInt(2, node.getPort()); | ||||
|                 exists.setLong(3, node.getStream()); | ||||
|                 if (exists.executeQuery().next()) { | ||||
|                     update.setLong(1, node.getServices()); | ||||
|                     update.setLong(2, node.getTime()); | ||||
|  | ||||
|                     update.setBytes(3, node.getIPv6()); | ||||
|                     update.setInt(4, node.getPort()); | ||||
|                     update.setLong(5, node.getStream()); | ||||
|                     update.executeUpdate(); | ||||
|                 } else { | ||||
|                     insert.setBytes(1, node.getIPv6()); | ||||
|                     insert.setInt(2, node.getPort()); | ||||
|                     insert.setLong(3, node.getServices()); | ||||
|                     insert.setLong(4, node.getStream()); | ||||
|                     insert.setLong(5, node.getTime()); | ||||
|                     insert.executeUpdate(); | ||||
|                 } | ||||
|             } | ||||
|         } catch (SQLException e) { | ||||
|             LOG.error(e.getMessage(), e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<InventoryVector> getInventory(long... streams) { | ||||
|         List<InventoryVector> result = new LinkedList<>(); | ||||
|         try { | ||||
|             Statement stmt = getConnection().createStatement(); | ||||
|             ResultSet rs = stmt.executeQuery("SELECT hash FROM Inventory WHERE expires > " + now() + | ||||
|                     " AND stream IN (" + join(streams) + ")"); | ||||
|             while (rs.next()) { | ||||
|                 result.add(new InventoryVector(rs.getBytes("hash"))); | ||||
|             } | ||||
|         } catch (SQLException e) { | ||||
|             LOG.error(e.getMessage(), e); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams) { | ||||
|         offer.removeAll(getInventory(streams)); | ||||
|         return offer; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public ObjectMessage getObject(InventoryVector vector) { | ||||
|         try { | ||||
|             Statement stmt = getConnection().createStatement(); | ||||
|             ResultSet rs = stmt.executeQuery("SELECT data, version FROM Inventory WHERE hash = " + vector); | ||||
|             Blob data = rs.getBlob("data"); | ||||
|             return Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length()); | ||||
|         } catch (Exception e) { | ||||
|             LOG.error(e.getMessage(), e); | ||||
|             throw new RuntimeException(e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void storeObject(int version, ObjectMessage object) { | ||||
|         try { | ||||
|             PreparedStatement ps = getConnection().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()); | ||||
|             ps.setLong(2, object.getStream()); | ||||
|             ps.setLong(3, object.getExpiresTime()); | ||||
|             ByteArrayOutputStream os = new ByteArrayOutputStream(); | ||||
|             object.write(os); | ||||
|             ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); | ||||
|             ps.setBlob(4, is); | ||||
|             ps.setLong(5, object.getType()); | ||||
|             ps.setInt(6, version); | ||||
|             ps.executeUpdate(); | ||||
|         } catch (SQLException e) { | ||||
|             LOG.error("Error storing object of type " + object.getPayload().getClass().getSimpleName(), e); | ||||
|         } catch (Exception e) { | ||||
|             LOG.error(e.getMessage(), e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected void writeBlob(PreparedStatement ps, int parameterIndex, Streamable data) throws SQLException, IOException { | ||||
|         ByteArrayOutputStream os = new ByteArrayOutputStream(); | ||||
|         data.write(os); | ||||
|         ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); | ||||
|         ps.setBlob(parameterIndex, is); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void cleanup() { | ||||
|         try { | ||||
|             // 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)); | ||||
|         } catch (SQLException e) { | ||||
|             LOG.debug(e.getMessage(), e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected Connection getConnection() { | ||||
|         try { | ||||
|             return DriverManager.getConnection(DB_URL, DB_USER, DB_PWD); | ||||
|         } catch (SQLException e) { | ||||
|             throw new RuntimeException(e); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -32,8 +32,8 @@ import java.util.List; | ||||
| /** | ||||
|  * Created by chris on 23.04.15. | ||||
|  */ | ||||
| public class JdbcAddressRepository extends DatabaseRepository implements AddressRepository { | ||||
|     private static final Logger LOG = LoggerFactory.getLogger(DatabaseRepository.class); | ||||
| public class JdbcAddressRepository extends JdbcHelper implements AddressRepository { | ||||
|     private static final Logger LOG = LoggerFactory.getLogger(JdbcAddressRepository.class); | ||||
|  | ||||
|     @Override | ||||
|     public List<BitmessageAddress> findIdentities() { | ||||
|   | ||||
| @@ -0,0 +1,63 @@ | ||||
| /* | ||||
|  * 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.inventory; | ||||
|  | ||||
| import ch.dissem.bitmessage.entity.Streamable; | ||||
| import org.flywaydb.core.Flyway; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import java.io.ByteArrayInputStream; | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.sql.Connection; | ||||
| import java.sql.DriverManager; | ||||
| import java.sql.PreparedStatement; | ||||
| import java.sql.SQLException; | ||||
|  | ||||
| /** | ||||
|  * 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; | ||||
|  | ||||
|  | ||||
|     static { | ||||
|         Flyway flyway = new Flyway(); | ||||
|         flyway.setDataSource(DB_URL, DB_USER, null); | ||||
|         flyway.migrate(); | ||||
|     } | ||||
|  | ||||
|     protected void writeBlob(PreparedStatement ps, int parameterIndex, Streamable data) throws SQLException, IOException { | ||||
|         ByteArrayOutputStream os = new ByteArrayOutputStream(); | ||||
|         data.write(os); | ||||
|         ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); | ||||
|         ps.setBlob(parameterIndex, is); | ||||
|     } | ||||
|  | ||||
|     protected Connection getConnection() { | ||||
|         try { | ||||
|             return DriverManager.getConnection(DB_URL, DB_USER, DB_PWD); | ||||
|         } catch (SQLException e) { | ||||
|             throw new RuntimeException(e); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,103 @@ | ||||
| /* | ||||
|  * 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.inventory; | ||||
|  | ||||
| import ch.dissem.bitmessage.entity.ObjectMessage; | ||||
| import ch.dissem.bitmessage.entity.valueobject.InventoryVector; | ||||
| import ch.dissem.bitmessage.factory.Factory; | ||||
| import ch.dissem.bitmessage.ports.Inventory; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| 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); | ||||
|  | ||||
|     @Override | ||||
|     public List<InventoryVector> getInventory(long... streams) { | ||||
|         List<InventoryVector> result = new LinkedList<>(); | ||||
|         try { | ||||
|             Statement stmt = getConnection().createStatement(); | ||||
|             ResultSet rs = stmt.executeQuery("SELECT hash FROM Inventory WHERE expires > " + now() + | ||||
|                     " AND stream IN (" + join(streams) + ")"); | ||||
|             while (rs.next()) { | ||||
|                 result.add(new InventoryVector(rs.getBytes("hash"))); | ||||
|             } | ||||
|         } catch (SQLException e) { | ||||
|             LOG.error(e.getMessage(), e); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams) { | ||||
|         offer.removeAll(getInventory(streams)); | ||||
|         return offer; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public ObjectMessage getObject(InventoryVector vector) { | ||||
|         try { | ||||
|             Statement stmt = getConnection().createStatement(); | ||||
|             ResultSet rs = stmt.executeQuery("SELECT data, version FROM Inventory WHERE hash = " + vector); | ||||
|             Blob data = rs.getBlob("data"); | ||||
|             return Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length()); | ||||
|         } catch (Exception e) { | ||||
|             LOG.error(e.getMessage(), e); | ||||
|             throw new RuntimeException(e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void storeObject(int version, ObjectMessage object) { | ||||
|         try { | ||||
|             PreparedStatement ps = getConnection().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()); | ||||
|             ps.setLong(2, object.getStream()); | ||||
|             ps.setLong(3, object.getExpiresTime()); | ||||
|             writeBlob(ps, 4, object); | ||||
|             ps.setLong(5, object.getType()); | ||||
|             ps.setInt(6, version); | ||||
|             ps.executeUpdate(); | ||||
|         } catch (SQLException e) { | ||||
|             LOG.error("Error storing object of type " + object.getPayload().getClass().getSimpleName(), e); | ||||
|         } catch (Exception e) { | ||||
|             LOG.error(e.getMessage(), e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void cleanup() { | ||||
|         try { | ||||
|             // 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)); | ||||
|         } catch (SQLException e) { | ||||
|             LOG.debug(e.getMessage(), e); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,95 @@ | ||||
| /* | ||||
|  * 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.inventory; | ||||
|  | ||||
| import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | ||||
| import ch.dissem.bitmessage.ports.NodeRegistry; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import java.sql.*; | ||||
| import java.util.LinkedList; | ||||
| import java.util.List; | ||||
|  | ||||
| import static ch.dissem.bitmessage.utils.Strings.join; | ||||
|  | ||||
| /** | ||||
|  * Created by chris on 24.04.15. | ||||
|  */ | ||||
| public class JdbcNodeRegistry extends JdbcHelper implements NodeRegistry { | ||||
|     private static final Logger LOG = LoggerFactory.getLogger(JdbcNodeRegistry.class); | ||||
|  | ||||
|     @Override | ||||
|     public List<NetworkAddress> getKnownAddresses(int limit, long... streams) { | ||||
|         List<NetworkAddress> result = new LinkedList<>(); | ||||
|         try { | ||||
|             Statement stmt = getConnection().createStatement(); | ||||
|             ResultSet rs = stmt.executeQuery("SELECT * FROM Node WHERE Stream IN (" + join(streams) + ")"); | ||||
|             while (rs.next()) { | ||||
| //                result.add(new NetworkAddress.Builder() | ||||
| //                        .ipv6(rs.getBytes("ip")) | ||||
| //                        .port(rs.getByte("port")) | ||||
| //                        .services(rs.getLong("services")) | ||||
| //                        .stream(rs.getLong("stream")) | ||||
| //                        .time(rs.getLong("time")) | ||||
| //                        .build()); | ||||
|             } | ||||
|         } catch (SQLException e) { | ||||
|             LOG.error(e.getMessage(), e); | ||||
|         } | ||||
|         if (result.isEmpty()) { | ||||
|             // FIXME: this is for testing purposes, remove it! | ||||
|             result.add(new NetworkAddress.Builder().ipv4(127, 0, 0, 1).port(8444).build()); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void offerAddresses(List<NetworkAddress> addresses) { | ||||
|         try { | ||||
|             Connection connection = getConnection(); | ||||
|             PreparedStatement exists = connection.prepareStatement("SELECT port FROM Node WHERE ip = ? AND port = ? AND stream = ?"); | ||||
|             PreparedStatement insert = connection.prepareStatement( | ||||
|                     "INSERT INTO Node (ip, port, services, stream, time) VALUES (?, ?, ?, ?, ?)"); | ||||
|             PreparedStatement update = connection.prepareStatement( | ||||
|                     "UPDATE Node SET services = ?, time = ? WHERE ip = ? AND port = ? AND stream = ?"); | ||||
|             for (NetworkAddress node : addresses) { | ||||
|                 exists.setBytes(1, node.getIPv6()); | ||||
|                 exists.setInt(2, node.getPort()); | ||||
|                 exists.setLong(3, node.getStream()); | ||||
|                 if (exists.executeQuery().next()) { | ||||
|                     update.setLong(1, node.getServices()); | ||||
|                     update.setLong(2, node.getTime()); | ||||
|  | ||||
|                     update.setBytes(3, node.getIPv6()); | ||||
|                     update.setInt(4, node.getPort()); | ||||
|                     update.setLong(5, node.getStream()); | ||||
|                     update.executeUpdate(); | ||||
|                 } else { | ||||
|                     insert.setBytes(1, node.getIPv6()); | ||||
|                     insert.setInt(2, node.getPort()); | ||||
|                     insert.setLong(3, node.getServices()); | ||||
|                     insert.setLong(4, node.getStream()); | ||||
|                     insert.setLong(5, node.getTime()); | ||||
|                     insert.executeUpdate(); | ||||
|                 } | ||||
|             } | ||||
|         } catch (SQLException e) { | ||||
|             LOG.error(e.getMessage(), e); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -16,7 +16,7 @@ | ||||
|  | ||||
| package ch.dissem.bitmessage.networking; | ||||
|  | ||||
| import ch.dissem.bitmessage.Context; | ||||
| import ch.dissem.bitmessage.BitmessageContext; | ||||
| import ch.dissem.bitmessage.entity.*; | ||||
| import ch.dissem.bitmessage.entity.valueobject.InventoryVector; | ||||
| import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | ||||
| @@ -43,7 +43,7 @@ import static ch.dissem.bitmessage.networking.Connection.State.*; | ||||
| public class Connection implements Runnable { | ||||
|     private final static Logger LOG = LoggerFactory.getLogger(Connection.class); | ||||
|  | ||||
|     private Context ctx; | ||||
|     private BitmessageContext ctx; | ||||
|  | ||||
|     private State state; | ||||
|     private Socket socket; | ||||
| @@ -59,8 +59,8 @@ public class Connection implements Runnable { | ||||
|  | ||||
|     private Queue<MessagePayload> sendingQueue = new ConcurrentLinkedDeque<>(); | ||||
|  | ||||
|     public Connection(State state, Socket socket, MessageListener listener) throws IOException { | ||||
|         this.ctx = Context.getInstance(); | ||||
|     public Connection(BitmessageContext context, State state, Socket socket, MessageListener listener) throws IOException { | ||||
|         this.ctx = context; | ||||
|         this.state = state; | ||||
|         this.socket = socket; | ||||
|         this.in = socket.getInputStream(); | ||||
| @@ -97,7 +97,7 @@ public class Connection implements Runnable { | ||||
|                         switch (msg.getPayload().getCommand()) { | ||||
|                             case VERSION: | ||||
|                                 Version payload = (Version) msg.getPayload(); | ||||
|                                 if (payload.getVersion() >= Context.CURRENT_VERSION) { | ||||
|                                 if (payload.getVersion() >= BitmessageContext.CURRENT_VERSION) { | ||||
|                                     this.version = payload.getVersion(); | ||||
|                                     this.streams = payload.getStreams(); | ||||
|                                     send(new VerAck()); | ||||
|   | ||||
| @@ -16,7 +16,8 @@ | ||||
|  | ||||
| package ch.dissem.bitmessage.networking; | ||||
|  | ||||
| import ch.dissem.bitmessage.Context; | ||||
| import ch.dissem.bitmessage.BitmessageContext; | ||||
| import ch.dissem.bitmessage.BitmessageContext.ContextHolder; | ||||
| import ch.dissem.bitmessage.entity.payload.ObjectPayload; | ||||
| import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | ||||
| import ch.dissem.bitmessage.ports.NetworkHandler; | ||||
| @@ -37,8 +38,9 @@ import static ch.dissem.bitmessage.networking.Connection.State.*; | ||||
| /** | ||||
|  * Handles all the networky stuff. | ||||
|  */ | ||||
| public class NetworkNode implements NetworkHandler { | ||||
| public class NetworkNode implements NetworkHandler, ContextHolder { | ||||
|     private final static Logger LOG = LoggerFactory.getLogger(NetworkNode.class); | ||||
|     private BitmessageContext ctx; | ||||
|     private final ExecutorService pool; | ||||
|     private final List<Connection> connections = new LinkedList<>(); | ||||
|     private ServerSocket serverSocket; | ||||
| @@ -48,21 +50,25 @@ public class NetworkNode implements NetworkHandler { | ||||
|         pool = Executors.newCachedThreadPool(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void setContext(BitmessageContext context) { | ||||
|         this.ctx = context; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void start(final MessageListener listener) { | ||||
|         final Context ctx = Context.getInstance(); | ||||
|         if (listener == null) { | ||||
|             throw new IllegalStateException("Listener must be set at start"); | ||||
|         } | ||||
|         try { | ||||
|             serverSocket = new ServerSocket(Context.getInstance().getPort()); | ||||
|             serverSocket = new ServerSocket(ctx.getPort()); | ||||
|             pool.execute(new Runnable() { | ||||
|                 @Override | ||||
|                 public void run() { | ||||
|                     try { | ||||
|                         Socket socket = serverSocket.accept(); | ||||
|                         socket.setSoTimeout(10000); | ||||
|                         startConnection(new Connection(SERVER, socket, listener)); | ||||
|                         startConnection(new Connection(ctx, SERVER, socket, listener)); | ||||
|                     } catch (IOException e) { | ||||
|                         LOG.debug(e.getMessage(), e); | ||||
|                     } | ||||
| @@ -85,7 +91,7 @@ public class NetworkNode implements NetworkHandler { | ||||
|                             List<NetworkAddress> addresses = ctx.getAddressRepository().getKnownAddresses(8, ctx.getStreams()); | ||||
|                             for (NetworkAddress address : addresses) { | ||||
|                                 try { | ||||
|                                     startConnection(new Connection(CLIENT, new Socket(address.toInetAddress(), address.getPort()), listener)); | ||||
|                                     startConnection(new Connection(ctx, CLIENT, new Socket(address.toInetAddress(), address.getPort()), listener)); | ||||
|                                 } catch (IOException e) { | ||||
|                                     LOG.debug(e.getMessage(), e); | ||||
|                                 } | ||||
|   | ||||
| @@ -18,9 +18,7 @@ package ch.dissem.bitmessage.networking; | ||||
|  | ||||
| import ch.dissem.bitmessage.entity.NetworkMessage; | ||||
| import ch.dissem.bitmessage.entity.Version; | ||||
| import ch.dissem.bitmessage.entity.payload.ObjectPayload; | ||||
| import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | ||||
| import ch.dissem.bitmessage.ports.NetworkHandler; | ||||
| import ch.dissem.bitmessage.utils.UnixTime; | ||||
| import org.junit.Test; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user