package network; import java.net.InetAddress; import java.net.ServerSocket; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Observable; import java.util.Observer; import java.util.SortedSet; import java.util.TreeSet; import java.util.logging.Logger; import utils.ObserverMessage; import controller.Controller; import network.message.Message; import network.message.MessageFactory; public class Network extends Observable implements ConnectionCallback { public static final int PORT = 9084; //public static final int PORT = 4809; private static final int MAX_HANDLED_MESSAGES_SIZE = 10000; private ConnectionCreator creator; private ConnectionAcceptor acceptor; private List<Peer> connectedPeers; private SortedSet<String> handledMessages; private boolean run; public Network() { this.connectedPeers = new ArrayList<Peer>(); this.run = true; this.start(); } private void start() { this.handledMessages = Collections.synchronizedSortedSet(new TreeSet<String>()); //START ConnectionCreator THREAD creator = new ConnectionCreator(this); creator.start(); //START ConnectionAcceptor THREAD acceptor = new ConnectionAcceptor(this); acceptor.start(); } @Override public void onConnect(Peer peer) { Logger.getGlobal().info("Connection successfull : " + peer.getAddress()); //ADD TO CONNECTED PEERS synchronized(this.connectedPeers) { this.connectedPeers.add(peer); } //ADD TO WHITELIST PeerManager.getInstance().addPeer(peer); //PASS TO CONTROLLER Controller.getInstance().onConnect(peer); //NOTIFY OBSERVERS this.setChanged(); this.notifyObservers(new ObserverMessage(ObserverMessage.ADD_PEER_TYPE, peer)); this.setChanged(); this.notifyObservers(new ObserverMessage(ObserverMessage.LIST_PEER_TYPE, this.connectedPeers)); } @Override public void onDisconnect(Peer peer) { Logger.getGlobal().info("Connection close : " + peer.getAddress()); //REMOVE FROM CONNECTED PEERS synchronized(this.connectedPeers) { this.connectedPeers.remove(peer); } //PASS TO CONTROLLER Controller.getInstance().onDisconnect(peer); //CLOSE CONNECTION IF STILL ACTIVE peer.close(); peer.interrupt(); //NOTIFY OBSERVERS this.setChanged(); this.notifyObservers(new ObserverMessage(ObserverMessage.REMOVE_PEER_TYPE, peer)); this.setChanged(); this.notifyObservers(new ObserverMessage(ObserverMessage.LIST_PEER_TYPE, this.connectedPeers)); } @Override public void onError(Peer peer) { Logger.getGlobal().warning("Connection error : " + peer.getAddress()); //REMOVE FROM CONNECTED PEERS synchronized(this.connectedPeers) { this.connectedPeers.remove(peer); } //ADD TO BLACKLIST PeerManager.getInstance().blacklistPeer(peer); //PASS TO CONTROLLER Controller.getInstance().onError(peer); //CLOSE CONNECTION IF STILL ACTIVE peer.close(); peer.interrupt(); //NOTIFY OBSERVERS this.setChanged(); this.notifyObservers(new ObserverMessage(ObserverMessage.REMOVE_PEER_TYPE, peer)); this.setChanged(); this.notifyObservers(new ObserverMessage(ObserverMessage.LIST_PEER_TYPE, this.connectedPeers)); } @Override public boolean isConnectedTo(InetAddress address) { try { synchronized(this.connectedPeers) { //FOR ALL connectedPeers for(Peer connectedPeer: connectedPeers) { //CHECK IF ADDRESS IS THE SAME if(address.equals(connectedPeer.getAddress())) { return true; } } } } catch(Exception e) { //CONCURRENCY ERROR } return false; } @Override public boolean isConnectedTo(Peer peer) { return this.isConnectedTo(peer.getAddress()); } @Override public List<Peer> getActiveConnections() { return this.connectedPeers; } private void addHandledMessage(byte[] hash) { try { synchronized(this.handledMessages) { //CHECK IF LIST IS FULL if(this.handledMessages.size() > MAX_HANDLED_MESSAGES_SIZE) { this.handledMessages.remove(this.handledMessages.first()); } this.handledMessages.add(new String(hash)); } } catch(Exception e) { e.printStackTrace(); } } @Override public void onMessage(Message message) { //CHECK IF WE ARE STILL PROCESSING MESSAGES if(!this.run) { return; } //ONLY HANDLE BLOCK AND TRANSACTION MESSAGES ONCE if(message.getType() == Message.TRANSACTION_TYPE || message.getType() == Message.BLOCK_TYPE) { synchronized(this.handledMessages) { //CHECK IF NOT HANDLED ALREADY if(this.handledMessages.contains(new String(message.getHash()))) { return; } //ADD TO HANDLED MESSAGES this.addHandledMessage(message.getHash()); } } switch(message.getType()) { //PING case Message.PING_TYPE: //CREATE PING Message response = MessageFactory.getInstance().createPingMessage(); //SET ID response.setId(message.getId()); //SEND BACK TO SENDER message.getSender().sendMessage(response); break; //GETPEERS case Message.GET_PEERS_TYPE: //CREATE NEW PEERS MESSAGE WITH PEERS Message answer = MessageFactory.getInstance().createPeersMessage(PeerManager.getInstance().getKnownPeers()); answer.setId(message.getId()); //SEND TO SENDER message.getSender().sendMessage(answer); break; //SEND TO CONTROLLER default: Controller.getInstance().onMessage(message); break; } } public void broadcast(Message message, List<Peer> exclude) { Logger.getGlobal().info("Broadcasting"); try { for(int i=0; i < this.connectedPeers.size() ; i++) { Peer peer = this.connectedPeers.get(i); //EXCLUDE PEERS if(peer != null && !exclude.contains(peer)) { peer.sendMessage(message); } } } catch(Exception e) { //error broadcasting e.printStackTrace(); } Logger.getGlobal().info("Broadcasting end"); } @Override public void addObserver(Observer o) { super.addObserver(o); //SEND CONNECTEDPEERS ON REGISTER o.update(this, new ObserverMessage(ObserverMessage.LIST_PEER_TYPE, this.connectedPeers)); } public static boolean isPortAvailable(int port) { try { ServerSocket socket = new ServerSocket(port); socket.close(); return true; } catch (Exception e) { return false; } } public void stop() { this.run = false; this.onMessage(null); } }