package ibis.ipl.registry.central.client; import ibis.io.Conversion; import ibis.ipl.Credentials; import ibis.ipl.IbisConfigurationException; import ibis.ipl.IbisProperties; import ibis.ipl.impl.Ibis; import ibis.ipl.impl.IbisIdentifier; import ibis.ipl.impl.Location; import ibis.ipl.registry.central.Event; import ibis.ipl.registry.central.Protocol; import ibis.ipl.registry.central.RegistryProperties; import ibis.ipl.registry.statistics.Statistics; import ibis.ipl.server.ServerProperties; 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 java.net.InetAddress; import java.net.InetSocketAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; final class CommunicationHandler implements Runnable { private static final int CONNECTION_BACKLOG = 10; private static final int MAX_THREADS = 10; private static final Logger logger = LoggerFactory .getLogger(CommunicationHandler.class); // private final byte[] version; private final Heartbeat heartbeat; private final Client client; private final VirtualSocketFactory virtualSocketFactory; private final VirtualServerSocket serverSocket; private final VirtualSocketAddress serverAddress; private final Pool pool; private final TypedProperties properties; private final int timeout; private final Statistics statistics; // communication settings private final boolean peerBootstrap; private final boolean gossip; private final boolean tree; // bootstrap data private IbisIdentifier identifier; private IbisIdentifier[] bootstrapList; private int joinTime; private int currentNrOfThreads = 0; private int maxNrOfThreads = 0; CommunicationHandler(TypedProperties properties, Pool pool, Statistics statistics) throws IOException, IbisConfigurationException { this.properties = properties; this.pool = pool; this.statistics = statistics; if (properties.getProperty(IbisProperties.SERVER_ADDRESS) == null) { throw new IbisConfigurationException( "cannot initialize registry, property " + IbisProperties.SERVER_ADDRESS + " is not specified"); } gossip = properties.getBooleanProperty(RegistryProperties.GOSSIP); tree = properties.getBooleanProperty(RegistryProperties.TREE); if (gossip && tree) { throw new IbisConfigurationException( "enabling both gossip and tree communication not allowed"); } if (gossip) { peerBootstrap = true; } else if (tree) { peerBootstrap = false; if (properties .getBooleanProperty(RegistryProperties.PEER_BOOTSTRAP)) { throw new IbisConfigurationException( "peer bootstrap not possible in combination with tree"); } } else { peerBootstrap = properties .getBooleanProperty(RegistryProperties.PEER_BOOTSTRAP); } timeout = properties .getIntProperty(RegistryProperties.CLIENT_CONNECT_TIMEOUT) * 1000; String clientID = this.properties.getProperty(Ibis.ID_PROPERTY); client = Client.getOrCreateClient(clientID, properties, 0); virtualSocketFactory = client.getFactory(); serverSocket = virtualSocketFactory.createServerSocket( Protocol.VIRTUAL_PORT, CONNECTION_BACKLOG, null); serverAddress = client.getServiceAddress(Protocol.VIRTUAL_PORT); if (serverAddress == null) { throw new IOException("could not get address of server"); } logger.debug("local address = " + serverSocket.getLocalSocketAddress()); logger.debug("server address = " + serverAddress); // init heartbeat long heartbeatInterval = properties .getIntProperty(RegistryProperties.HEARTBEAT_INTERVAL) * 1000; boolean exitOnServerFailure = properties.getBooleanProperty(RegistryProperties.EXIT_ON_SERVER_FAILURE); heartbeat = new Heartbeat(this, pool, heartbeatInterval, exitOnServerFailure); // init gossiper (if needed) if (gossip) { long gossipInterval = properties .getIntProperty(RegistryProperties.GOSSIP_INTERVAL) * 1000; new Gossiper(this, pool, gossipInterval); } // init broadcaster (if needed) if (tree) { Thread eventPusher = new IterativeEventPusher(pool, this); eventPusher.setDaemon(true); eventPusher.start(); } } synchronized IbisIdentifier getIdentifier() { return identifier; } /** * connects to the registry server, joins, and gets back the identifier of * this Ibis and some bootstrap information * * @param applicationTag * A tag for this ibis provided by the application * * @throws IOException * in case of trouble */ IbisIdentifier join(byte[] implementationData, String implementationVersion, Credentials credentials, byte[] tag) throws IOException { long start = System.currentTimeMillis(); long heartbeatInterval = properties .getIntProperty(RegistryProperties.HEARTBEAT_INTERVAL) * 1000; long eventPushInterval = properties .getIntProperty(RegistryProperties.EVENT_PUSH_INTERVAL) * 1000; long gossipInterval = properties .getIntProperty(RegistryProperties.GOSSIP_INTERVAL) * 1000; boolean adaptGossipInterval = properties .getBooleanProperty(RegistryProperties.ADAPT_GOSSIP_INTERVAL); boolean keepStatistics = properties .getBooleanProperty(RegistryProperties.STATISTICS); long statisticsInterval = properties .getIntProperty(RegistryProperties.STATISTICS_INTERVAL) * 1000; boolean purgeHistory = properties .getBooleanProperty(RegistryProperties.PURGE_HISTORY); VirtualSocketAddress address = serverSocket.getLocalSocketAddress(); // We try to generate an array of global IP addresses here. We use these // in an attempt to get a more reasonable location. InetAddress[] preferred = null; if (address.machine().hasPublicAddress()) { InetSocketAddress[] sa = address.machine().getPublicAddresses(); preferred = new InetAddress[sa.length]; for (int i = 0; i < sa.length; i++) { preferred[i] = sa[i].getAddress(); } } Location location = Location.defaultLocation(properties, preferred); byte[] myAddress = address.toBytes(); logger.debug("joining to " + pool.getName() + ", connecting to server"); Connection connection; try { connection = new Connection(serverAddress, timeout, true, virtualSocketFactory); } catch (IOException e) { throw new IbisConfigurationException("Cannot connect to server at " + serverAddress + ", please check if it has been started"); } logger.debug("sending join info to server"); try { connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_JOIN); connection.out().writeUTF(ServerProperties.implementationVersion); connection.out().writeInt(myAddress.length); connection.out().write(myAddress); connection.out().writeUTF(pool.getName()); connection.out().writeInt(implementationData.length); connection.out().write(implementationData); connection.out().writeUTF(implementationVersion); location.writeTo(connection.out()); connection.out().writeBoolean(peerBootstrap); connection.out().writeLong(heartbeatInterval); connection.out().writeLong(eventPushInterval); connection.out().writeBoolean(gossip); connection.out().writeLong(gossipInterval); connection.out().writeBoolean(adaptGossipInterval); connection.out().writeBoolean(tree); connection.out().writeBoolean(pool.isClosedWorld()); connection.out().writeInt(pool.getSize()); connection.out().writeBoolean(keepStatistics); connection.out().writeLong(statisticsInterval); connection.out().writeBoolean(purgeHistory); byte[] credentialBytes = Conversion.object2byte(credentials); connection.out().writeInt(credentialBytes.length); connection.out().write(credentialBytes); if (tag == null) { connection.out().writeInt(-1); } else { connection.out().writeInt(tag.length); connection.out().write(tag); } logger.debug("reading join result info from server"); connection.getAndCheckReply(); IbisIdentifier identifier = new IbisIdentifier(connection.in()); int joinTime = connection.in().readInt(); int startOfEventListTime = connection.in().readInt(); int listLength = connection.in().readInt(); IbisIdentifier[] bootstrapList = new IbisIdentifier[listLength]; for (int i = 0; i < listLength; i++) { bootstrapList[i] = new IbisIdentifier(connection.in()); } connection.close(); heartbeat.resetDeadlines(); synchronized (this) { this.identifier = identifier; this.bootstrapList = bootstrapList; this.joinTime = joinTime; } // start saving event from the time the server expects pool.purgeHistoryUpto(startOfEventListTime); logger.debug("join done, identifier = " + identifier); long end = System.currentTimeMillis(); if (statistics != null) { statistics.add(Protocol.OPCODE_JOIN, end - start, connection .read(), connection.written(), false); } return identifier; } catch (IOException e) { // join failed connection.close(); throw e; } finally { // start handling connections createThread(); } } Client getClient() { return client; } void bootstrap() throws IOException { if (!peerBootstrap) { // we will receive bootstrap data from a push/forward/broadcast return; } long start = System.currentTimeMillis(); IbisIdentifier identifier; IbisIdentifier[] bootstrapList; int joinTime; synchronized (this) { identifier = this.identifier; bootstrapList = this.bootstrapList; joinTime = this.joinTime; } for (IbisIdentifier ibis : bootstrapList) { if (!ibis.equals(identifier)) { logger.debug("trying to bootstrap with data from " + ibis); Connection connection = null; try { connection = new Connection(ibis, timeout, false, virtualSocketFactory, Protocol.VIRTUAL_PORT); connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_GET_STATE); identifier.writeTo(connection.out()); connection.out().writeInt(joinTime); connection.out().flush(); connection.getAndCheckReply(); pool.init(connection.in()); long end = System.currentTimeMillis(); if (statistics != null) { statistics.add(Protocol.OPCODE_GET_STATE, end - start, connection.read(), connection.written(), false); } return; } catch (Exception e) { logger.info("bootstrap with " + ibis + " failed, trying next one", e); } finally { if (connection != null) { connection.close(); } } } } logger .debug("could not bootstrap registry with any peer, trying server"); Connection connection = new Connection(serverAddress, timeout, true, virtualSocketFactory); try { connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_GET_STATE); identifier.writeTo(connection.out()); connection.out().writeInt(joinTime); connection.out().flush(); connection.getAndCheckReply(); pool.init(connection.in()); connection.close(); heartbeat.resetDeadlines(); long end = System.currentTimeMillis(); if (statistics != null) { statistics.add(Protocol.OPCODE_GET_STATE, end - start, connection.read(), connection.written(), false); } } catch (IOException e) { connection.close(); throw e; } } public void signal(String signal, ibis.ipl.IbisIdentifier... ibisses) throws IOException { long start = System.currentTimeMillis(); Connection connection = new Connection(serverAddress, timeout, true, virtualSocketFactory); try { connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_SIGNAL); getIdentifier().writeTo(connection.out()); connection.out().writeUTF(signal); connection.out().writeInt(ibisses.length); for (int i = 0; i < ibisses.length; i++) { ((IbisIdentifier) ibisses[i]).writeTo(connection.out()); } connection.out().flush(); connection.getAndCheckReply(); connection.close(); logger.debug("done telling " + ibisses.length + " ibisses a string: " + signal); heartbeat.resetDeadlines(); long end = System.currentTimeMillis(); if (statistics != null) { statistics.add(Protocol.OPCODE_SIGNAL, end - start, connection .read(), connection.written(), false); } } catch (IOException e) { connection.close(); throw e; } } public void terminate() throws IOException { long start = System.currentTimeMillis(); Connection connection = new Connection(serverAddress, timeout, true, virtualSocketFactory); try { connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_TERMINATE); getIdentifier().writeTo(connection.out()); connection.out().flush(); connection.getAndCheckReply(); connection.close(); logger.debug("done terminating"); heartbeat.resetDeadlines(); long end = System.currentTimeMillis(); if (statistics != null) { statistics.add(Protocol.OPCODE_TERMINATE, end - start, connection.read(), connection.written(), false); } } catch (IOException e) { connection.close(); throw e; } } public long getSeqno(String name) throws IOException { long start = System.currentTimeMillis(); if (pool.isStopped()) { throw new IOException( "cannot get sequence number, registry already stopped"); } logger.debug("getting sequence number"); Connection connection = new Connection(serverAddress, timeout, true, virtualSocketFactory); try { connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_SEQUENCE_NR); getIdentifier().writeTo(connection.out()); connection.out().writeUTF(name); connection.out().flush(); connection.getAndCheckReply(); long result = connection.in().readLong(); connection.close(); logger.debug("sequence number = " + result); heartbeat.resetDeadlines(); long end = System.currentTimeMillis(); if (statistics != null) { statistics.add(Protocol.OPCODE_SEQUENCE_NR, end - start, connection.read(), connection.written(), false); } return result; } catch (IOException e) { connection.close(); throw e; } } public void assumeDead(ibis.ipl.IbisIdentifier ibis) throws IOException { long start = System.currentTimeMillis(); if (pool.isStopped()) { throw new IOException( "cannot do assumeDead, registry already stopped"); } logger.debug("declaring " + ibis + " to be dead"); Connection connection = new Connection(serverAddress, timeout, true, virtualSocketFactory); try { connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_DEAD); getIdentifier().writeTo(connection.out()); ((IbisIdentifier) ibis).writeTo(connection.out()); connection.out().flush(); connection.getAndCheckReply(); connection.close(); logger.debug("done declaring " + ibis + " dead "); heartbeat.resetDeadlines(); long end = System.currentTimeMillis(); if (statistics != null) { statistics.add(Protocol.OPCODE_DEAD, end - start, connection .read(), connection.written(), false); } } catch (IOException e) { connection.close(); throw e; } } public void maybeDead(ibis.ipl.IbisIdentifier ibis) throws IOException { long start = System.currentTimeMillis(); if (pool.isStopped()) { throw new IOException( "cannot do maybeDead, registry already stopped"); } logger.debug("reporting " + ibis + " to possibly be dead"); Connection connection = new Connection(serverAddress, timeout, true, virtualSocketFactory); try { connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_MAYBE_DEAD); getIdentifier().writeTo(connection.out()); ((IbisIdentifier) ibis).writeTo(connection.out()); connection.out().flush(); connection.getAndCheckReply(); connection.close(); logger.debug("done reporting " + ibis + " to possibly be dead"); heartbeat.resetDeadlines(); long end = System.currentTimeMillis(); if (statistics != null) { statistics.add(Protocol.OPCODE_MAYBE_DEAD, end - start, connection.read(), connection.written(), false); } } catch (IOException e) { connection.close(); throw e; } } /** * Contact server, to get new events, and to let the server know we are * still alive * * @return true if sending the heartbeat succeeded, or was unnecessary, or * false if sending the heartbeat failed * */ boolean sendHeartBeat() { long start = System.currentTimeMillis(); if (getIdentifier() == null) { // not joined yet return true; } if (pool.isStopped()) { // pool already stopped return true; } logger.debug("sending heartbeat to server"); Connection connection = null; try { connection = new Connection(serverAddress, timeout, true, virtualSocketFactory); connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_HEARTBEAT); getIdentifier().writeTo(connection.out()); connection.out().flush(); connection.getAndCheckReply(); connection.close(); long end = System.currentTimeMillis(); if (statistics != null) { statistics.add(Protocol.OPCODE_HEARTBEAT, end - start, connection.read(), connection.written(), false); } logger.debug("send heartbeat"); return true; } catch (Exception e) { if (connection != null) { connection.close(); } logger.info(identifier + ": could not send heartbeat to server", e); return false; } } public void leave() throws IOException { logger.debug("leaving pool"); long start = System.currentTimeMillis(); Connection connection = new Connection(serverAddress, timeout, true, virtualSocketFactory); try { connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_LEAVE); getIdentifier().writeTo(connection.out()); connection.out().flush(); connection.getAndCheckReply(); connection.close(); heartbeat.resetDeadlines(); long end = System.currentTimeMillis(); if (statistics != null) { statistics.add(Protocol.OPCODE_LEAVE, end - start, connection .read(), connection.written(), false); } logger.debug("left"); } finally { connection.close(); pool.stop(); end(); heartbeat.nudge(); } } public IbisIdentifier elect(String election, long timeout) throws IOException { long start = System.currentTimeMillis(); if (timeout > Integer.MAX_VALUE) { timeout = Integer.MAX_VALUE; } if (timeout == 0) { // we are allowed to wait forever, but no need to wait more than // until we are sure we should have connected with the server timeout = this.timeout; } Connection connection = new Connection(serverAddress, (int) timeout, true, virtualSocketFactory); try { connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_ELECT); getIdentifier().writeTo(connection.out()); connection.out().writeUTF(election); connection.out().flush(); connection.getAndCheckReply(); IbisIdentifier winner = new IbisIdentifier(connection.in()); connection.close(); logger.debug("election : \"" + election + "\" done, result = " + winner); heartbeat.resetDeadlines(); long end = System.currentTimeMillis(); if (statistics != null) { statistics.add(Protocol.OPCODE_ELECT, end - start, connection .read(), connection.written(), false); } return winner; } catch (IOException e) { connection.close(); throw e; } } void gossip(IbisIdentifier ibis) throws IOException { long start = System.currentTimeMillis(); if (ibis.equals(getIdentifier())) { logger.debug("not gossiping with self"); return; } if (pool.isStopped()) { return; } logger.debug("gossiping with " + ibis); Connection connection = new Connection(ibis, timeout, false, virtualSocketFactory, Protocol.VIRTUAL_PORT); try { connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(Protocol.OPCODE_GOSSIP); getIdentifier().writeTo(connection.out()); int localTime = pool.getTime(); connection.out().writeInt(localTime); connection.out().flush(); connection.getAndCheckReply(); int peerTime = connection.in().readInt(); Event[] newEvents = null; if (peerTime > localTime) { logger.debug("localtime = " + localTime + ", peerTime = " + peerTime + ", receiving events"); int nrOfEvents = connection.in().readInt(); if (nrOfEvents > 0) { newEvents = new Event[nrOfEvents]; for (int i = 0; i < newEvents.length; i++) { newEvents[i] = new Event(connection.in()); } } } else if (peerTime < localTime) { logger.debug("localtime = " + localTime + ", peerTime = " + peerTime + ", pushing events"); Event[] sendEvents = pool.getEventsFrom(peerTime); connection.out().writeInt(sendEvents.length); for (Event event : sendEvents) { event.writeTo(connection.out()); } } else { // nothing to send either way } logger.debug("gossiping with " + ibis + " done, time now: " + pool.getTime()); connection.close(); if (newEvents != null) { pool.newEventsReceived(newEvents); } long end = System.currentTimeMillis(); if (statistics != null) { statistics.add(Protocol.OPCODE_GOSSIP, end - start, connection .read(), connection.written(), false); } } catch (IOException e) { connection.close(); throw e; } } void forward(IbisIdentifier ibis) { byte opcode = Protocol.OPCODE_FORWARD; long start = System.currentTimeMillis(); if (ibis.equals(getIdentifier())) { logger.debug("not forwarding events to self"); return; } if (pool.isStopped()) { return; } logger.debug(identifier + ": forwarding to: " + ibis); Connection connection = null; try { logger.debug("creating connection to push events to " + ibis); connection = new Connection(ibis, timeout, false, virtualSocketFactory, Protocol.VIRTUAL_PORT); logger.debug("connection to " + ibis + " created"); connection.out().writeByte(Protocol.MAGIC_BYTE); connection.out().writeByte(opcode); connection.out().writeUTF(pool.getName()); connection.out().flush(); logger.debug("waiting for peer time of peer " + ibis); boolean requestBootstrap = connection.in().readBoolean(); int peerJoinTime = connection.in().readInt(); int requestedEventTime = connection.in().readInt(); logger.debug("request bootstrap = " + requestBootstrap + ", peerJoinTime = " + peerJoinTime + ", requested event time = " + requestedEventTime); connection.sendOKReply(); // send bootstrap (if needed) if (requestBootstrap) { logger.debug("sending state"); pool.writeState(connection.out(), peerJoinTime); } Event[] events = pool.getEventsFrom(requestedEventTime); logger.debug("sending " + events.length + " entries to " + ibis); connection.out().writeInt(events.length); for (int i = 0; i < events.length; i++) { events[i].writeTo(connection.out()); } // no updated of minimum time connection.out().writeInt(-1); connection.close(); logger.debug("connection to " + ibis + " closed"); if (statistics != null) { statistics.add(opcode, System.currentTimeMillis() - start, connection.read(), connection.written(), false); } } catch (IOException e) { if (pool.isMember(ibis)) { logger.error("cannot reach " + ibis + " to push events to", e); } } finally { if (connection != null) { connection.close(); } } } private void handleGossip(Connection connection) throws IOException { logger.debug("got a gossip request"); IbisIdentifier identifier = new IbisIdentifier(connection.in()); String poolName = identifier.poolName(); int peerTime = connection.in().readInt(); if (!poolName.equals(pool.getName())) { logger.error("wrong pool: " + poolName + " instead of " + pool.getName()); connection.closeWithError("wrong pool: " + poolName + " instead of " + pool.getName()); return; } int localTime = pool.getTime(); connection.sendOKReply(); connection.out().writeInt(localTime); connection.out().flush(); if (localTime > peerTime) { Event[] sendEvents = pool.getEventsFrom(peerTime); connection.out().writeInt(sendEvents.length); for (Event event : sendEvents) { event.writeTo(connection.out()); } connection.out().flush(); } else if (localTime < peerTime) { int nrOfEvents = connection.in().readInt(); if (nrOfEvents > 0) { Event[] newEvents = new Event[nrOfEvents]; for (int i = 0; i < newEvents.length; i++) { newEvents[i] = new Event(connection.in()); } connection.close(); pool.newEventsReceived(newEvents); } } connection.close(); } private void handlePush(Connection connection, byte opcode) throws IOException { Event[] newEvents = null; logger.debug("got a push/forward/broadcast"); long start = System.currentTimeMillis(); String poolName = connection.in().readUTF(); long readPoolName = System.currentTimeMillis(); if (!poolName.equals(pool.getName())) { logger.error("wrong pool: " + poolName + " instead of " + pool.getName()); connection.closeWithError("wrong pool: " + poolName + " instead of " + pool.getName()); } boolean requestBootstrap = !peerBootstrap && !pool.isInitialized(); int nextRequiredEvent = pool.getNextRequiredEvent(); int joinTime; long gatheredPoolData = System.currentTimeMillis(); synchronized (this) { joinTime = this.joinTime; } long gatheredData = System.currentTimeMillis(); connection.out().writeBoolean(requestBootstrap); connection.out().writeInt(joinTime); connection.out().writeInt(nextRequiredEvent); connection.out().flush(); long sendData = System.currentTimeMillis(); connection.getAndCheckReply(); long gotReply = System.currentTimeMillis(); if (requestBootstrap) { logger.debug("recieving bootstrap in push"); // we should receive the bootstrap data next pool.init(connection.in()); } long readBootstrap = System.currentTimeMillis(); int events = connection.in().readInt(); logger.debug("receiving " + events + " events"); if (events < 0) { connection.closeWithError("negative event value"); return; } newEvents = new Event[events]; for (int i = 0; i < newEvents.length; i++) { newEvents[i] = new Event(connection.in()); if (logger.isDebugEnabled()) { logger.debug("received event " + newEvents[i]); } } int minEventTime = connection.in().readInt(); long readEvents = System.currentTimeMillis(); connection.close(); long closedConnection = System.currentTimeMillis(); if (newEvents != null) { pool.newEventsReceived(newEvents); } if (minEventTime != -1) { pool.purgeHistoryUpto(minEventTime); } long done = System.currentTimeMillis(); if (opcode == Protocol.OPCODE_BROADCAST) { logger.info("readPoolName = " + (readPoolName - start) + ", gatheredPoolData = " + (gatheredPoolData - readPoolName) + ", gatheredData = " + (gatheredData - gatheredPoolData) + ", sendData = " + (sendData - gatheredData) + ", gotReply = " + (gotReply - sendData) + ", readBootstrap = " + (readBootstrap - gotReply) + ", readEvents = " + (readEvents - readBootstrap) + ", closedConnection = " + (closedConnection - readEvents) + ", done = " + (done - closedConnection)); } logger.debug("push handled"); } private void handlePing(Connection connection) throws IOException { logger.debug("got a ping request"); IbisIdentifier identifier = getIdentifier(); if (identifier == null) { connection.closeWithError("ibis identifier not initialized yet"); return; } connection.sendOKReply(); getIdentifier().writeTo(connection.out()); connection.out().flush(); connection.close(); } private void handleGetState(Connection connection) throws IOException { logger.debug("got a state request"); IbisIdentifier identifier = new IbisIdentifier(connection.in()); int joinTime = connection.in().readInt(); String poolName = identifier.poolName(); if (!poolName.equals(pool.getName())) { logger.error("wrong pool: " + poolName + " instead of " + pool.getName()); connection.closeWithError("wrong pool: " + poolName + " instead of " + pool.getName()); return; } if (!pool.isInitialized()) { connection.closeWithError("state not available"); return; } connection.sendOKReply(); pool.writeState(connection.out(), joinTime); connection.out().flush(); connection.close(); } private synchronized void createThread() { while (currentNrOfThreads >= MAX_THREADS) { try { wait(); } catch (InterruptedException e) { // IGNORE } } // create new thread for next connection ThreadPool.createNew(this, "client 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 (pool.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; } try { long start = System.currentTimeMillis(); byte magic = connection.in().readByte(); if (magic != Protocol.MAGIC_BYTE) { throw new IOException( "Invalid header byte in accepting connection"); } byte opcode = connection.in().readByte(); if (opcode < Protocol.NR_OF_OPCODES) { logger.debug("received request: " + Protocol.OPCODE_NAMES[opcode]); } switch (opcode) { case Protocol.OPCODE_GOSSIP: handleGossip(connection); break; case Protocol.OPCODE_PUSH: case Protocol.OPCODE_BROADCAST: case Protocol.OPCODE_FORWARD: handlePush(connection, opcode); break; case Protocol.OPCODE_PING: handlePing(connection); break; case Protocol.OPCODE_GET_STATE: handleGetState(connection); break; default: logger.error("unknown opcode in request: " + opcode); } logger.debug("done handling request"); long end = System.currentTimeMillis(); if (statistics != null) { statistics.add(opcode, end - start, connection.read(), connection.written(), true); } } catch (Throwable e) { logger.error("error on handling request", e); } finally { connection.close(); } threadEnded(); } void end() { try { serverSocket.close(); } catch (Exception e) { // IGNORE } try { virtualSocketFactory.end(); } catch (Exception e) { // IGNORE } } }