package ibis.ipl.registry.gossip; import ibis.ipl.IbisConfigurationException; import ibis.ipl.impl.Ibis; import ibis.ipl.impl.IbisIdentifier; import ibis.ipl.registry.statistics.Statistics; import ibis.ipl.support.Client; import ibis.ipl.support.Connection; import ibis.smartsockets.virtual.VirtualServerSocket; import ibis.smartsockets.virtual.VirtualSocketAddress; import ibis.smartsockets.virtual.VirtualSocketFactory; import ibis.util.ThreadPool; import ibis.util.TypedProperties; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class CommunicationHandler implements Runnable { private static final int CONNECTION_BACKLOG = 25; static final int MAX_THREADS = 25; private static final int LEAVE_CONNECTION_TIMEOUT = 1000; private static final int CONNECTION_TIMEOUT = 5000; private static final Logger logger = LoggerFactory .getLogger(CommunicationHandler.class); private final Registry registry; private final Statistics statistics; private final MemberSet pool; private final ElectionSet elections; private final VirtualSocketFactory socketFactory; private final VirtualServerSocket serverSocket; private final ARRG arrg; private final int nrOfLeavesSend; private final int gossipSize; private int currentNrOfThreads = 0; private int maxNrOfThreads = 0; CommunicationHandler(TypedProperties properties, Registry registry, MemberSet members, ElectionSet elections, Statistics statistics) throws IbisConfigurationException, IOException { this.registry = registry; this.pool = members; this.elections = elections; this.statistics = statistics; nrOfLeavesSend = properties .getIntProperty(RegistryProperties.LEAVES_SEND); gossipSize = properties.getIntProperty(RegistryProperties.GOSSIP_COUNT); String clientID = properties.getProperty(Ibis.ID_PROPERTY); Client client = Client.getOrCreateClient(clientID, properties, 0); socketFactory = client.getFactory(); serverSocket = socketFactory.createServerSocket(0, CONNECTION_BACKLOG, null); VirtualSocketAddress serverAddress = client .getServiceAddress(Protocol.VIRTUAL_PORT); String[] bootstrapStringList = properties .getStringList(RegistryProperties.BOOTSTRAP_LIST); VirtualSocketAddress[] bootstrapList = new VirtualSocketAddress[bootstrapStringList.length]; for (int i = 0; i < bootstrapList.length; i++) { bootstrapList[i] = new VirtualSocketAddress(bootstrapStringList[i]); } logger.debug("local address = " + serverSocket.getLocalSocketAddress()); logger.debug("server address = " + serverAddress); arrg = new ARRG(serverSocket.getLocalSocketAddress(), false, bootstrapList, serverAddress, registry.getPoolName(), socketFactory, statistics); } public void start() { arrg.start(); createThread(); } public void sendSignals(String signal, IbisIdentifier[] ibises) throws IOException { String errorMessage = null; for (IbisIdentifier ibis : ibises) { try { long start = System.currentTimeMillis(); Connection connection = new Connection(ibis, CONNECTION_TIMEOUT, true, socketFactory, Protocol.VIRTUAL_PORT); connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_SIGNAL); registry.getIbisIdentifier().writeTo(connection.out()); connection.out().writeUTF(signal); connection.getAndCheckReply(); connection.close(); if (statistics != null) { statistics.add(Protocol.OPCODE_SIGNAL, System .currentTimeMillis() - start, connection.read(), connection.written(), false); } } catch (IOException e) { logger.error("could not send signal to " + ibis); if (errorMessage == null) { errorMessage = "could not send signal to: " + ibis; } else { errorMessage += ", " + ibis; } } } if (errorMessage != null) { throw new IOException(errorMessage); } } private void handleSignal(Connection connection) throws IOException { IbisIdentifier source = new IbisIdentifier(connection.in()); String signal = connection.in().readUTF(); connection.sendOKReply(); registry.signal(signal, source); connection.close(); } public void gossip() { VirtualSocketAddress address = arrg.getRandomMember(); if (address == null || address.equals(serverSocket.getLocalSocketAddress())) { logger.debug("noone to gossip with, or (not) gossiping with self"); return; } try { long start = System.currentTimeMillis(); Connection connection = new Connection(address, CONNECTION_TIMEOUT, true, socketFactory); connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_GOSSIP); registry.getIbisIdentifier().writeTo(connection.out()); pool.writeGossipData(connection.out(), gossipSize); elections.writeGossipData(connection.out()); connection.getAndCheckReply(); pool.readGossipData(connection.in()); elections.readGossipData(connection.in()); connection.close(); if (statistics != null) { statistics .add(Protocol.OPCODE_GOSSIP, System.currentTimeMillis() - start, connection.read(), connection .written(), false); } } catch (IOException e) { logger.debug("could not gossip with " + address, e); } } private void handleGossip(Connection connection) throws IOException { IbisIdentifier peer = new IbisIdentifier(connection.in()); if (peer.equals(registry.getIbisIdentifier())) { logger.error("eep! talking to ourselves"); connection.closeWithError("talking to self"); } if (!peer.poolName().equals(registry.getIbisIdentifier().poolName())) { connection.closeWithError("wrong pool"); } pool.readGossipData(connection.in()); elections.readGossipData(connection.in()); connection.sendOKReply(); pool.writeGossipData(connection.out(), gossipSize); elections.writeGossipData(connection.out()); connection.close(); } /** * Sends leave message to everyone ARRG knows :) */ public void broadcastLeave() { VirtualSocketAddress[] addresses = arrg .getRandomMembers(nrOfLeavesSend); Broadcaster broadcaster = new Broadcaster(this, addresses); // wait for all the broadcasts to be finished broadcaster.waitUntilDone(); } void sendLeave(VirtualSocketAddress address) { if (address.equals(serverSocket.getLocalSocketAddress())) { // do not connect to self return; } try { long start = System.currentTimeMillis(); Connection connection = new Connection(address, LEAVE_CONNECTION_TIMEOUT, true, socketFactory); connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_LEAVE); registry.getIbisIdentifier().writeTo(connection.out()); connection.getAndCheckReply(); connection.close(); if (statistics != null) { statistics .add(Protocol.OPCODE_LEAVE, System.currentTimeMillis() - start, connection.read(), connection .written(), false); } } catch (IOException e) { logger.debug(serverSocket.getLocalSocketAddress() + " could not send leave to " + address); } } private void handleLeave(Connection connection) throws IOException { IbisIdentifier ibis = new IbisIdentifier(connection.in()); connection.sendOKReply(); pool.leave(ibis); connection.close(); } public void ping(IbisIdentifier ibis) throws IOException { long start = System.currentTimeMillis(); Connection connection = new Connection(ibis, CONNECTION_TIMEOUT, true, socketFactory, Protocol.VIRTUAL_PORT); connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_PING); connection.getAndCheckReply(); IbisIdentifier result = new IbisIdentifier(connection.in()); connection.close(); if (!result.equals(ibis)) { throw new IOException("tried to ping " + ibis + ", reached " + result + " instead"); } if (statistics != null) { statistics.add(Protocol.OPCODE_PING, System.currentTimeMillis() - start, connection.read(), connection.written(), false); } } private void handlePing(Connection connection) throws IOException { connection.sendOKReply(); registry.getIbisIdentifier().writeTo(connection.out()); } // ARRG gossip send and handled in ARRG class private void handleArrgGossip(Connection connection) throws IOException { String poolName = connection.in().readUTF(); if (!poolName.equals(registry.getPoolName())) { connection.closeWithError("wrong pool name"); return; } arrg.handleGossip(connection); } private synchronized void createThread() { while (currentNrOfThreads >= MAX_THREADS) { try { wait(); } catch (InterruptedException e) { // IGNORE } } // create new thread for next connection ThreadPool.createNew(this, "connection handler"); currentNrOfThreads++; if (currentNrOfThreads > maxNrOfThreads) { maxNrOfThreads = currentNrOfThreads; } } private synchronized void threadEnded() { currentNrOfThreads--; notifyAll(); } public void run() { Connection connection = null; try { logger.debug("accepting connection"); connection = new Connection(serverSocket); logger.debug("connection accepted"); } catch (IOException e) { if (registry.isStopped()) { threadEnded(); return; } logger.error("Accept failed, waiting a second, will retry", e); // wait a bit try { Thread.sleep(1000); } catch (InterruptedException e1) { // IGNORE } } // create new thread for next connection createThread(); if (connection == null) { threadEnded(); return; } long start = System.currentTimeMillis(); byte opcode = 0; try { byte magic = connection.in().readByte(); if (magic != Protocol.MAGIC_BYTE) { throw new IOException( "Invalid header byte in accepting connection"); } opcode = connection.in().readByte(); if (logger.isDebugEnabled()) { logger.debug("got request, opcode = " + Protocol.opcodeString(opcode)); } switch (opcode) { case Protocol.OPCODE_ARRG_GOSSIP: handleArrgGossip(connection); break; case Protocol.OPCODE_SIGNAL: handleSignal(connection); break; case Protocol.OPCODE_LEAVE: handleLeave(connection); break; case Protocol.OPCODE_GOSSIP: handleGossip(connection); break; case Protocol.OPCODE_PING: handlePing(connection); break; default: logger.error("unknown opcode: " + opcode); } } catch (IOException e) { logger.error("error on handling connection", e); } finally { connection.close(); } logger.debug("done handling request"); if (statistics != null) { statistics.add(opcode, System.currentTimeMillis() - start, connection.read(), connection.written(), true); } threadEnded(); } VirtualSocketAddress getAddress() { return serverSocket.getLocalSocketAddress(); } }