Code cleanup & improvements
- most notably removed some unnecessary synchronize blocks in the DefaultNetworkHandler
This commit is contained in:
		@@ -302,9 +302,6 @@ public class BitmessageContext {
 | 
			
		||||
        long connectionTTL = 30 * MINUTE;
 | 
			
		||||
        boolean sendPubkeyOnIdentityCreation = true;
 | 
			
		||||
 | 
			
		||||
        public Builder() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Builder port(int port) {
 | 
			
		||||
            this.port = port;
 | 
			
		||||
            return this;
 | 
			
		||||
 
 | 
			
		||||
@@ -55,9 +55,6 @@ public class Addr implements MessagePayload {
 | 
			
		||||
    public static final class Builder {
 | 
			
		||||
        private List<NetworkAddress> addresses = new ArrayList<NetworkAddress>();
 | 
			
		||||
 | 
			
		||||
        public Builder() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Builder addresses(Collection<NetworkAddress> addresses){
 | 
			
		||||
            this.addresses.addAll(addresses);
 | 
			
		||||
            return this;
 | 
			
		||||
 
 | 
			
		||||
@@ -188,7 +188,7 @@ public class BitmessageAddress implements Serializable {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return alias != null ? alias : address;
 | 
			
		||||
        return alias == null ? address : alias;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] getRipe() {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,8 @@ import java.util.List;
 | 
			
		||||
 * The 'getdata' command is used to request objects from a node.
 | 
			
		||||
 */
 | 
			
		||||
public class GetData implements MessagePayload {
 | 
			
		||||
    public static final int MAX_INVENTORY_SIZE = 50_000;
 | 
			
		||||
 | 
			
		||||
    List<InventoryVector> inventory;
 | 
			
		||||
 | 
			
		||||
    private GetData(Builder builder) {
 | 
			
		||||
@@ -54,9 +56,6 @@ public class GetData implements MessagePayload {
 | 
			
		||||
    public static final class Builder {
 | 
			
		||||
        private List<InventoryVector> inventory = new LinkedList<>();
 | 
			
		||||
 | 
			
		||||
        public Builder() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Builder addInventoryVector(InventoryVector inventoryVector) {
 | 
			
		||||
            this.inventory.add(inventoryVector);
 | 
			
		||||
            return this;
 | 
			
		||||
 
 | 
			
		||||
@@ -54,9 +54,6 @@ public class Inv implements MessagePayload {
 | 
			
		||||
    public static final class Builder {
 | 
			
		||||
        private List<InventoryVector> inventory = new LinkedList<>();
 | 
			
		||||
 | 
			
		||||
        public Builder() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Builder addInventoryVector(InventoryVector inventoryVector) {
 | 
			
		||||
            this.inventory.add(inventoryVector);
 | 
			
		||||
            return this;
 | 
			
		||||
 
 | 
			
		||||
@@ -156,10 +156,10 @@ public class ObjectMessage implements MessagePayload {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void write(OutputStream out) throws IOException {
 | 
			
		||||
        if (nonce != null) {
 | 
			
		||||
            out.write(nonce);
 | 
			
		||||
        } else {
 | 
			
		||||
        if (nonce == null) {
 | 
			
		||||
            out.write(new byte[8]);
 | 
			
		||||
        } else {
 | 
			
		||||
            out.write(nonce);
 | 
			
		||||
        }
 | 
			
		||||
        out.write(getPayloadBytesWithoutNonce());
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -143,9 +143,6 @@ public class Version implements MessagePayload {
 | 
			
		||||
        private String userAgent;
 | 
			
		||||
        private long[] streamNumbers;
 | 
			
		||||
 | 
			
		||||
        public Builder() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Builder defaults() {
 | 
			
		||||
            version = BitmessageContext.CURRENT_VERSION;
 | 
			
		||||
            services = 1;
 | 
			
		||||
 
 | 
			
		||||
@@ -96,9 +96,6 @@ public class V2Pubkey extends Pubkey {
 | 
			
		||||
        private byte[] publicSigningKey;
 | 
			
		||||
        private byte[] publicEncryptionKey;
 | 
			
		||||
 | 
			
		||||
        public Builder() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Builder stream(long streamNumber) {
 | 
			
		||||
            this.streamNumber = streamNumber;
 | 
			
		||||
            return this;
 | 
			
		||||
 
 | 
			
		||||
@@ -123,9 +123,6 @@ public class V3Pubkey extends V2Pubkey {
 | 
			
		||||
        private long extraBytes;
 | 
			
		||||
        private byte[] signature = new byte[0];
 | 
			
		||||
 | 
			
		||||
        public Builder() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Builder stream(long streamNumber) {
 | 
			
		||||
            this.streamNumber = streamNumber;
 | 
			
		||||
            return this;
 | 
			
		||||
 
 | 
			
		||||
@@ -134,9 +134,6 @@ public class NetworkAddress implements Streamable {
 | 
			
		||||
        private byte[] ipv6;
 | 
			
		||||
        private int port;
 | 
			
		||||
 | 
			
		||||
        public Builder() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Builder time(final long time) {
 | 
			
		||||
            this.time = time;
 | 
			
		||||
            return this;
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ import ch.dissem.bitmessage.exception.ApplicationException;
 | 
			
		||||
import ch.dissem.bitmessage.utils.Bytes;
 | 
			
		||||
 | 
			
		||||
import java.security.MessageDigest;
 | 
			
		||||
import java.security.NoSuchAlgorithmException;
 | 
			
		||||
 | 
			
		||||
import static ch.dissem.bitmessage.utils.Bytes.inc;
 | 
			
		||||
 | 
			
		||||
@@ -33,18 +34,17 @@ import static ch.dissem.bitmessage.utils.Bytes.inc;
 | 
			
		||||
public class SimplePOWEngine implements ProofOfWorkEngine {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void calculateNonce(byte[] initialHash, byte[] target, Callback callback) {
 | 
			
		||||
        byte[] nonce = new byte[8];
 | 
			
		||||
        MessageDigest mda;
 | 
			
		||||
        try {
 | 
			
		||||
            mda = MessageDigest.getInstance("SHA-512");
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            throw new ApplicationException(e);
 | 
			
		||||
        }
 | 
			
		||||
            MessageDigest mda = MessageDigest.getInstance("SHA-512");
 | 
			
		||||
            byte[] nonce = new byte[8];
 | 
			
		||||
            do {
 | 
			
		||||
                inc(nonce);
 | 
			
		||||
                mda.update(nonce);
 | 
			
		||||
                mda.update(initialHash);
 | 
			
		||||
            } while (Bytes.lt(target, mda.digest(mda.digest()), 8));
 | 
			
		||||
            callback.onNonceCalculated(initialHash, nonce);
 | 
			
		||||
        } catch (NoSuchAlgorithmException e) {
 | 
			
		||||
            throw new ApplicationException(e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -289,18 +289,18 @@ public class Application {
 | 
			
		||||
        System.out.println("Stream:  " + address.getStream());
 | 
			
		||||
        System.out.println("Version: " + address.getVersion());
 | 
			
		||||
        if (address.getPrivateKey() == null) {
 | 
			
		||||
            if (address.getPubkey() != null) {
 | 
			
		||||
                System.out.println("Public key available");
 | 
			
		||||
            } else {
 | 
			
		||||
            if (address.getPubkey() == null) {
 | 
			
		||||
                System.out.println("Public key still missing");
 | 
			
		||||
            } else {
 | 
			
		||||
                System.out.println("Public key available");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void messages() {
 | 
			
		||||
        String command;
 | 
			
		||||
        List<Plaintext> messages = ctx.messages().findMessages(Plaintext.Status.RECEIVED);
 | 
			
		||||
        do {
 | 
			
		||||
            List<Plaintext> messages = ctx.messages().findMessages(Plaintext.Status.RECEIVED);
 | 
			
		||||
            System.out.println();
 | 
			
		||||
            int i = 0;
 | 
			
		||||
            for (Plaintext message : messages) {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,6 @@ import ch.dissem.bitmessage.InternalContext;
 | 
			
		||||
import ch.dissem.bitmessage.entity.*;
 | 
			
		||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
 | 
			
		||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
 | 
			
		||||
import ch.dissem.bitmessage.exception.ApplicationException;
 | 
			
		||||
import ch.dissem.bitmessage.exception.InsufficientProofOfWorkException;
 | 
			
		||||
import ch.dissem.bitmessage.exception.NodeException;
 | 
			
		||||
import ch.dissem.bitmessage.factory.Factory;
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,120 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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.networking;
 | 
			
		||||
 | 
			
		||||
import ch.dissem.bitmessage.InternalContext;
 | 
			
		||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
 | 
			
		||||
import ch.dissem.bitmessage.ports.NetworkHandler;
 | 
			
		||||
import ch.dissem.bitmessage.utils.UnixTime;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import static ch.dissem.bitmessage.networking.Connection.Mode.CLIENT;
 | 
			
		||||
import static ch.dissem.bitmessage.networking.DefaultNetworkHandler.NETWORK_MAGIC_NUMBER;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Christian Basler
 | 
			
		||||
 */
 | 
			
		||||
public class ConnectionOrganizer implements Runnable {
 | 
			
		||||
    private static final Logger LOG = LoggerFactory.getLogger(ConnectionOrganizer.class);
 | 
			
		||||
 | 
			
		||||
    private final InternalContext ctx;
 | 
			
		||||
    private final DefaultNetworkHandler networkHandler;
 | 
			
		||||
    private final NetworkHandler.MessageListener listener;
 | 
			
		||||
 | 
			
		||||
    private Connection initialConnection;
 | 
			
		||||
 | 
			
		||||
    public ConnectionOrganizer(InternalContext ctx,
 | 
			
		||||
                               DefaultNetworkHandler networkHandler,
 | 
			
		||||
                               NetworkHandler.MessageListener listener) {
 | 
			
		||||
        this.ctx = ctx;
 | 
			
		||||
        this.networkHandler = networkHandler;
 | 
			
		||||
        this.listener = listener;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void run() {
 | 
			
		||||
        try {
 | 
			
		||||
            while (networkHandler.isRunning()) {
 | 
			
		||||
                try {
 | 
			
		||||
                    int active = 0;
 | 
			
		||||
                    long now = UnixTime.now();
 | 
			
		||||
 | 
			
		||||
                    int diff = networkHandler.connections.size() - ctx.getConnectionLimit();
 | 
			
		||||
                    if (diff > 0) {
 | 
			
		||||
                        for (Connection c : networkHandler.connections) {
 | 
			
		||||
                            c.disconnect();
 | 
			
		||||
                            diff--;
 | 
			
		||||
                            if (diff == 0) break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    boolean forcedDisconnect = false;
 | 
			
		||||
                    for (Iterator<Connection> iterator = networkHandler.connections.iterator(); iterator.hasNext(); ) {
 | 
			
		||||
                        Connection c = iterator.next();
 | 
			
		||||
                        // Just in case they were all created at the same time, don't disconnect
 | 
			
		||||
                        // all at once.
 | 
			
		||||
                        if (!forcedDisconnect && now - c.getStartTime() > ctx.getConnectionTTL()) {
 | 
			
		||||
                            c.disconnect();
 | 
			
		||||
                            forcedDisconnect = true;
 | 
			
		||||
                        }
 | 
			
		||||
                        switch (c.getState()) {
 | 
			
		||||
                            case DISCONNECTED:
 | 
			
		||||
                                iterator.remove();
 | 
			
		||||
                                break;
 | 
			
		||||
                            case ACTIVE:
 | 
			
		||||
                                active++;
 | 
			
		||||
                                break;
 | 
			
		||||
                            default:
 | 
			
		||||
                                // nothing to do
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (active < NETWORK_MAGIC_NUMBER) {
 | 
			
		||||
                        List<NetworkAddress> addresses = ctx.getNodeRegistry().getKnownAddresses(
 | 
			
		||||
                                NETWORK_MAGIC_NUMBER - active, ctx.getStreams());
 | 
			
		||||
                        boolean first = active == 0 && initialConnection == null;
 | 
			
		||||
                        for (NetworkAddress address : addresses) {
 | 
			
		||||
                            Connection c = new Connection(ctx, CLIENT, address, listener, networkHandler.requestedObjects);
 | 
			
		||||
                            if (first) {
 | 
			
		||||
                                initialConnection = c;
 | 
			
		||||
                                first = false;
 | 
			
		||||
                            }
 | 
			
		||||
                            networkHandler.startConnection(c);
 | 
			
		||||
                        }
 | 
			
		||||
                        Thread.sleep(10000);
 | 
			
		||||
                    } else if (initialConnection != null) {
 | 
			
		||||
                        initialConnection.disconnect();
 | 
			
		||||
                        initialConnection = null;
 | 
			
		||||
                        Thread.sleep(10000);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Thread.sleep(30000);
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (InterruptedException e) {
 | 
			
		||||
                    networkHandler.stop();
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    LOG.error("Error in connection manager. Ignored.", e);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } finally {
 | 
			
		||||
            LOG.debug("Connection manager shutting down.");
 | 
			
		||||
            networkHandler.stop();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -22,25 +22,21 @@ import ch.dissem.bitmessage.entity.CustomMessage;
 | 
			
		||||
import ch.dissem.bitmessage.entity.GetData;
 | 
			
		||||
import ch.dissem.bitmessage.entity.NetworkMessage;
 | 
			
		||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
 | 
			
		||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
 | 
			
		||||
import ch.dissem.bitmessage.exception.ApplicationException;
 | 
			
		||||
import ch.dissem.bitmessage.exception.NodeException;
 | 
			
		||||
import ch.dissem.bitmessage.factory.Factory;
 | 
			
		||||
import ch.dissem.bitmessage.ports.NetworkHandler;
 | 
			
		||||
import ch.dissem.bitmessage.utils.Collections;
 | 
			
		||||
import ch.dissem.bitmessage.utils.Property;
 | 
			
		||||
import ch.dissem.bitmessage.utils.UnixTime;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.net.InetAddress;
 | 
			
		||||
import java.net.ServerSocket;
 | 
			
		||||
import java.net.Socket;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.concurrent.*;
 | 
			
		||||
 | 
			
		||||
import static ch.dissem.bitmessage.networking.Connection.Mode.CLIENT;
 | 
			
		||||
import static ch.dissem.bitmessage.networking.Connection.Mode.SERVER;
 | 
			
		||||
import static ch.dissem.bitmessage.networking.Connection.State.ACTIVE;
 | 
			
		||||
import static ch.dissem.bitmessage.utils.DebugUtils.inc;
 | 
			
		||||
@@ -54,13 +50,13 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
 | 
			
		||||
 | 
			
		||||
    public final static int NETWORK_MAGIC_NUMBER = 8;
 | 
			
		||||
 | 
			
		||||
    private final Collection<Connection> connections = new ConcurrentLinkedQueue<>();
 | 
			
		||||
    final Collection<Connection> connections = new ConcurrentLinkedQueue<>();
 | 
			
		||||
    private final ExecutorService pool;
 | 
			
		||||
    private InternalContext ctx;
 | 
			
		||||
    private ServerSocket serverSocket;
 | 
			
		||||
    private ServerRunnable server;
 | 
			
		||||
    private volatile boolean running;
 | 
			
		||||
 | 
			
		||||
    private Set<InventoryVector> requestedObjects = newSetFromMap(new ConcurrentHashMap<InventoryVector, Boolean>(50_000));
 | 
			
		||||
    final Set<InventoryVector> requestedObjects = newSetFromMap(new ConcurrentHashMap<InventoryVector, Boolean>(50_000));
 | 
			
		||||
 | 
			
		||||
    public DefaultNetworkHandler() {
 | 
			
		||||
        pool = Executors.newCachedThreadPool(new ThreadFactory() {
 | 
			
		||||
@@ -122,93 +118,9 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
 | 
			
		||||
        try {
 | 
			
		||||
            running = true;
 | 
			
		||||
            connections.clear();
 | 
			
		||||
            serverSocket = new ServerSocket(ctx.getPort());
 | 
			
		||||
            pool.execute(new Runnable() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void run() {
 | 
			
		||||
                    while (!serverSocket.isClosed()) {
 | 
			
		||||
                        try {
 | 
			
		||||
                            Socket socket = serverSocket.accept();
 | 
			
		||||
                            socket.setSoTimeout(Connection.READ_TIMEOUT);
 | 
			
		||||
                            startConnection(new Connection(ctx, SERVER, socket, listener, requestedObjects));
 | 
			
		||||
                        } catch (IOException e) {
 | 
			
		||||
                            LOG.debug(e.getMessage(), e);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            pool.execute(new Runnable() {
 | 
			
		||||
                public Connection initialConnection;
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void run() {
 | 
			
		||||
                    try {
 | 
			
		||||
                        while (running) {
 | 
			
		||||
                            try {
 | 
			
		||||
                                int active = 0;
 | 
			
		||||
                                long now = UnixTime.now();
 | 
			
		||||
                                synchronized (connections) {
 | 
			
		||||
                                    int diff = connections.size() - ctx.getConnectionLimit();
 | 
			
		||||
                                    if (diff > 0) {
 | 
			
		||||
                                        for (Connection c : connections) {
 | 
			
		||||
                                            c.disconnect();
 | 
			
		||||
                                            diff--;
 | 
			
		||||
                                            if (diff == 0) break;
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                    boolean forcedDisconnect = false;
 | 
			
		||||
                                    for (Iterator<Connection> iterator = connections.iterator(); iterator.hasNext(); ) {
 | 
			
		||||
                                        Connection c = iterator.next();
 | 
			
		||||
                                        // Just in case they were all created at the same time, don't disconnect
 | 
			
		||||
                                        // all at once.
 | 
			
		||||
                                        if (!forcedDisconnect && now - c.getStartTime() > ctx.getConnectionTTL()) {
 | 
			
		||||
                                            c.disconnect();
 | 
			
		||||
                                            forcedDisconnect = true;
 | 
			
		||||
                                        }
 | 
			
		||||
                                        switch (c.getState()) {
 | 
			
		||||
                                            case DISCONNECTED:
 | 
			
		||||
                                                iterator.remove();
 | 
			
		||||
                                                break;
 | 
			
		||||
                                            case ACTIVE:
 | 
			
		||||
                                                active++;
 | 
			
		||||
                                                break;
 | 
			
		||||
                                            default:
 | 
			
		||||
                                                // nothing to do
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                                if (active < NETWORK_MAGIC_NUMBER) {
 | 
			
		||||
                                    List<NetworkAddress> addresses = ctx.getNodeRegistry().getKnownAddresses(
 | 
			
		||||
                                            NETWORK_MAGIC_NUMBER - active, ctx.getStreams());
 | 
			
		||||
                                    boolean first = active == 0 && initialConnection == null;
 | 
			
		||||
                                    for (NetworkAddress address : addresses) {
 | 
			
		||||
                                        Connection c = new Connection(ctx, CLIENT, address, listener, requestedObjects);
 | 
			
		||||
                                        if (first) {
 | 
			
		||||
                                            initialConnection = c;
 | 
			
		||||
                                            first = false;
 | 
			
		||||
                                        }
 | 
			
		||||
                                        startConnection(c);
 | 
			
		||||
                                    }
 | 
			
		||||
                                    Thread.sleep(10000);
 | 
			
		||||
                                } else if (initialConnection != null) {
 | 
			
		||||
                                    initialConnection.disconnect();
 | 
			
		||||
                                    initialConnection = null;
 | 
			
		||||
                                    Thread.sleep(10000);
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    Thread.sleep(30000);
 | 
			
		||||
                                }
 | 
			
		||||
                            } catch (InterruptedException e) {
 | 
			
		||||
                                running = false;
 | 
			
		||||
                            } catch (Exception e) {
 | 
			
		||||
                                LOG.error("Error in connection manager. Ignored.", e);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } finally {
 | 
			
		||||
                        LOG.debug("Connection manager shutting down.");
 | 
			
		||||
                        running = false;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            server = new ServerRunnable(ctx, this, listener);
 | 
			
		||||
            pool.execute(server);
 | 
			
		||||
            pool.execute(new ConnectionOrganizer(ctx, this, listener));
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            throw new ApplicationException(e);
 | 
			
		||||
        }
 | 
			
		||||
@@ -221,13 +133,9 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void stop() {
 | 
			
		||||
        running = false;
 | 
			
		||||
        try {
 | 
			
		||||
            serverSocket.close();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            LOG.debug(e.getMessage(), e);
 | 
			
		||||
        }
 | 
			
		||||
        server.close();
 | 
			
		||||
        synchronized (connections) {
 | 
			
		||||
            running = false;
 | 
			
		||||
            for (Connection c : connections) {
 | 
			
		||||
                c.disconnect();
 | 
			
		||||
            }
 | 
			
		||||
@@ -235,8 +143,12 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
 | 
			
		||||
        requestedObjects.clear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void startConnection(Connection c) {
 | 
			
		||||
    void startConnection(Connection c) {
 | 
			
		||||
        if (!running) return;
 | 
			
		||||
 | 
			
		||||
        synchronized (connections) {
 | 
			
		||||
            if (!running) return;
 | 
			
		||||
 | 
			
		||||
            // prevent connecting twice to the same node
 | 
			
		||||
            if (connections.contains(c)) {
 | 
			
		||||
                return;
 | 
			
		||||
@@ -250,13 +162,11 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void offer(final InventoryVector iv) {
 | 
			
		||||
        List<Connection> target = new LinkedList<>();
 | 
			
		||||
        synchronized (connections) {
 | 
			
		||||
        for (Connection connection : connections) {
 | 
			
		||||
            if (connection.getState() == ACTIVE && !connection.knowsOf(iv)) {
 | 
			
		||||
                target.add(connection);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        }
 | 
			
		||||
        List<Connection> randomSubset = Collections.selectRandom(NETWORK_MAGIC_NUMBER, target);
 | 
			
		||||
        for (Connection connection : randomSubset) {
 | 
			
		||||
            connection.offer(iv);
 | 
			
		||||
@@ -269,7 +179,6 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
 | 
			
		||||
        TreeMap<Long, Integer> incomingConnections = new TreeMap<>();
 | 
			
		||||
        TreeMap<Long, Integer> outgoingConnections = new TreeMap<>();
 | 
			
		||||
 | 
			
		||||
        synchronized (connections) {
 | 
			
		||||
        for (Connection connection : connections) {
 | 
			
		||||
            if (connection.getState() == ACTIVE) {
 | 
			
		||||
                long stream = connection.getNode().getStream();
 | 
			
		||||
@@ -281,7 +190,6 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        }
 | 
			
		||||
        Property[] streamProperties = new Property[streams.size()];
 | 
			
		||||
        int i = 0;
 | 
			
		||||
        for (Long stream : streams) {
 | 
			
		||||
@@ -303,7 +211,7 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
 | 
			
		||||
 | 
			
		||||
    void request(Set<InventoryVector> inventoryVectors) {
 | 
			
		||||
        if (!running || inventoryVectors.isEmpty()) return;
 | 
			
		||||
        synchronized (connections) {
 | 
			
		||||
 | 
			
		||||
        Map<Connection, List<InventoryVector>> distribution = new HashMap<>();
 | 
			
		||||
        for (Connection connection : connections) {
 | 
			
		||||
            if (connection.getState() == ACTIVE) {
 | 
			
		||||
@@ -311,24 +219,19 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Iterator<InventoryVector> iterator = inventoryVectors.iterator();
 | 
			
		||||
            InventoryVector next;
 | 
			
		||||
            if (iterator.hasNext()) {
 | 
			
		||||
                next = iterator.next();
 | 
			
		||||
            } else {
 | 
			
		||||
        if (!iterator.hasNext()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
            boolean firstRound = true;
 | 
			
		||||
            while (firstRound || iterator.hasNext()) {
 | 
			
		||||
                if (!firstRound) {
 | 
			
		||||
                    next = iterator.next();
 | 
			
		||||
                    firstRound = true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    firstRound = false;
 | 
			
		||||
                }
 | 
			
		||||
        InventoryVector next = iterator.next();
 | 
			
		||||
        Connection previous = null;
 | 
			
		||||
        do {
 | 
			
		||||
            for (Connection connection : distribution.keySet()) {
 | 
			
		||||
                if (connection == previous) {
 | 
			
		||||
                    next = iterator.next();
 | 
			
		||||
                }
 | 
			
		||||
                if (connection.knowsOf(next)) {
 | 
			
		||||
                    List<InventoryVector> ivs = distribution.get(connection);
 | 
			
		||||
                        if (ivs.size() == 50_000) {
 | 
			
		||||
                    if (ivs.size() == GetData.MAX_INVENTORY_SIZE) {
 | 
			
		||||
                        connection.send(new GetData.Builder().inventory(ivs).build());
 | 
			
		||||
                        ivs.clear();
 | 
			
		||||
                    }
 | 
			
		||||
@@ -337,14 +240,14 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
 | 
			
		||||
 | 
			
		||||
                    if (iterator.hasNext()) {
 | 
			
		||||
                        next = iterator.next();
 | 
			
		||||
                            firstRound = true;
 | 
			
		||||
                        previous = connection;
 | 
			
		||||
                    } else {
 | 
			
		||||
                            firstRound = false;
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            }
 | 
			
		||||
        } while (iterator.hasNext());
 | 
			
		||||
 | 
			
		||||
        for (Connection connection : distribution.keySet()) {
 | 
			
		||||
            List<InventoryVector> ivs = distribution.get(connection);
 | 
			
		||||
            if (!ivs.isEmpty()) {
 | 
			
		||||
@@ -353,4 +256,3 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,70 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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.networking;
 | 
			
		||||
 | 
			
		||||
import ch.dissem.bitmessage.InternalContext;
 | 
			
		||||
import ch.dissem.bitmessage.ports.NetworkHandler;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import java.io.Closeable;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.net.ServerSocket;
 | 
			
		||||
import java.net.Socket;
 | 
			
		||||
 | 
			
		||||
import static ch.dissem.bitmessage.networking.Connection.Mode.SERVER;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Christian Basler
 | 
			
		||||
 */
 | 
			
		||||
public class ServerRunnable implements Runnable, Closeable {
 | 
			
		||||
    private static final Logger LOG = LoggerFactory.getLogger(ServerRunnable.class);
 | 
			
		||||
    private final InternalContext ctx;
 | 
			
		||||
    private final ServerSocket serverSocket;
 | 
			
		||||
    private final DefaultNetworkHandler networkHandler;
 | 
			
		||||
    private final NetworkHandler.MessageListener listener;
 | 
			
		||||
 | 
			
		||||
    public ServerRunnable(InternalContext ctx, DefaultNetworkHandler networkHandler, NetworkHandler.MessageListener listener) throws IOException {
 | 
			
		||||
        this.ctx = ctx;
 | 
			
		||||
        this.networkHandler = networkHandler;
 | 
			
		||||
        this.listener = listener;
 | 
			
		||||
        this.serverSocket = new ServerSocket(ctx.getPort());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void run() {
 | 
			
		||||
        while (!serverSocket.isClosed()) {
 | 
			
		||||
            try {
 | 
			
		||||
                Socket socket = serverSocket.accept();
 | 
			
		||||
                socket.setSoTimeout(Connection.READ_TIMEOUT);
 | 
			
		||||
                networkHandler.startConnection(new Connection(ctx, SERVER, socket, listener,
 | 
			
		||||
                        networkHandler.requestedObjects));
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                LOG.debug(e.getMessage(), e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void close() {
 | 
			
		||||
        try {
 | 
			
		||||
            serverSocket.close();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            LOG.debug(e.getMessage(), e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -99,10 +99,7 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito
 | 
			
		||||
                BitmessageAddress address;
 | 
			
		||||
 | 
			
		||||
                InputStream privateKeyStream = rs.getBinaryStream("private_key");
 | 
			
		||||
                if (privateKeyStream != null) {
 | 
			
		||||
                    PrivateKey privateKey = PrivateKey.read(privateKeyStream);
 | 
			
		||||
                    address = new BitmessageAddress(privateKey);
 | 
			
		||||
                } else {
 | 
			
		||||
                if (privateKeyStream == null) {
 | 
			
		||||
                    address = new BitmessageAddress(rs.getString("address"));
 | 
			
		||||
                    Blob publicKeyBlob = rs.getBlob("public_key");
 | 
			
		||||
                    if (publicKeyBlob != null) {
 | 
			
		||||
@@ -113,6 +110,9 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito
 | 
			
		||||
                        }
 | 
			
		||||
                        address.setPubkey(pubkey);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    PrivateKey privateKey = PrivateKey.read(privateKeyStream);
 | 
			
		||||
                    address = new BitmessageAddress(privateKey);
 | 
			
		||||
                }
 | 
			
		||||
                address.setAlias(rs.getString("alias"));
 | 
			
		||||
                address.setSubscribed(rs.getBoolean("subscribed"));
 | 
			
		||||
 
 | 
			
		||||
@@ -77,12 +77,12 @@ public abstract class JdbcHelper {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void writeBlob(PreparedStatement ps, int parameterIndex, Streamable data) throws SQLException, IOException {
 | 
			
		||||
        if (data != null) {
 | 
			
		||||
        if (data == null) {
 | 
			
		||||
            ps.setBytes(parameterIndex, null);
 | 
			
		||||
        } else {
 | 
			
		||||
            ByteArrayOutputStream os = new ByteArrayOutputStream();
 | 
			
		||||
            data.write(os);
 | 
			
		||||
            ps.setBytes(parameterIndex, os.toByteArray());
 | 
			
		||||
        } else {
 | 
			
		||||
            ps.setBytes(parameterIndex, null);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -90,10 +90,10 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito
 | 
			
		||||
    @Override
 | 
			
		||||
    public int countUnread(Label label) {
 | 
			
		||||
        String where;
 | 
			
		||||
        if (label != null) {
 | 
			
		||||
            where = "id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() + ") AND ";
 | 
			
		||||
        } else {
 | 
			
		||||
        if (label == null) {
 | 
			
		||||
            where = "";
 | 
			
		||||
        } else {
 | 
			
		||||
            where = "id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() + ") AND ";
 | 
			
		||||
        }
 | 
			
		||||
        where += "id IN (SELECT message_id FROM Message_Label WHERE label_id IN (" +
 | 
			
		||||
                "SELECT id FROM Label WHERE type = '" + Label.Type.UNREAD.name() + "'))";
 | 
			
		||||
@@ -237,14 +237,14 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito
 | 
			
		||||
        PreparedStatement ps = connection.prepareStatement(
 | 
			
		||||
                "INSERT INTO Message (iv, type, sender, recipient, data, sent, received, status, initial_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
 | 
			
		||||
                Statement.RETURN_GENERATED_KEYS);
 | 
			
		||||
        ps.setBytes(1, message.getInventoryVector() != null ? message.getInventoryVector().getHash() : null);
 | 
			
		||||
        ps.setBytes(1, message.getInventoryVector() == null ? null : message.getInventoryVector().getHash());
 | 
			
		||||
        ps.setString(2, message.getType().name());
 | 
			
		||||
        ps.setString(3, message.getFrom().getAddress());
 | 
			
		||||
        ps.setString(4, message.getTo() != null ? message.getTo().getAddress() : null);
 | 
			
		||||
        ps.setString(4, message.getTo() == null ? null : message.getTo().getAddress());
 | 
			
		||||
        writeBlob(ps, 5, message);
 | 
			
		||||
        ps.setLong(6, message.getSent());
 | 
			
		||||
        ps.setLong(7, message.getReceived());
 | 
			
		||||
        ps.setString(8, message.getStatus() != null ? message.getStatus().name() : null);
 | 
			
		||||
        ps.setString(8, message.getStatus() == null ? null : message.getStatus().name());
 | 
			
		||||
        ps.setBytes(9, message.getInitialHash());
 | 
			
		||||
 | 
			
		||||
        ps.executeUpdate();
 | 
			
		||||
@@ -258,10 +258,10 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito
 | 
			
		||||
    private void update(Connection connection, Plaintext message) throws SQLException, IOException {
 | 
			
		||||
        PreparedStatement ps = connection.prepareStatement(
 | 
			
		||||
                "UPDATE Message SET iv=?, sent=?, received=?, status=?, initial_hash=? WHERE id=?");
 | 
			
		||||
        ps.setBytes(1, message.getInventoryVector() != null ? message.getInventoryVector().getHash() : null);
 | 
			
		||||
        ps.setBytes(1, message.getInventoryVector() == null ? null : message.getInventoryVector().getHash());
 | 
			
		||||
        ps.setLong(2, message.getSent());
 | 
			
		||||
        ps.setLong(3, message.getReceived());
 | 
			
		||||
        ps.setString(4, message.getStatus() != null ? message.getStatus().name() : null);
 | 
			
		||||
        ps.setString(4, message.getStatus() == null ? null : message.getStatus().name());
 | 
			
		||||
        ps.setBytes(5, message.getInitialHash());
 | 
			
		||||
        ps.setLong(6, (Long) message.getId());
 | 
			
		||||
        ps.executeUpdate();
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user