package ibis.ipl.registry.gossip; import ibis.ipl.registry.statistics.Statistics; import ibis.ipl.server.ServerProperties; import ibis.ipl.server.Service; 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.util.HashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BootstrapService implements Service, Runnable { private static final int CONNECTION_BACKLOG = 50; static final int MAX_THREADS = 50; private static final Logger logger = LoggerFactory .getLogger(BootstrapService.class); private final VirtualServerSocket serverSocket; private final VirtualSocketFactory socketFactory; private final Map<String, ARRG> arrgs; // private final boolean printEvents; // private final boolean printEvents; private final boolean printErrors; private final boolean keepStatistics; private boolean ended = false; private int currentNrOfThreads = 0; private int maxNrOfThreads = 0; public BootstrapService(TypedProperties properties, VirtualSocketFactory socketFactory) throws IOException { this.socketFactory = socketFactory; printErrors = properties .getBooleanProperty(ServerProperties.PRINT_ERRORS); //printEvents = properties.getBooleanProperty(ServerProperties.PRINT_EVENTS); keepStatistics = properties .getBooleanProperty(RegistryProperties.STATISTICS); arrgs = new HashMap<String, ARRG>(); serverSocket = socketFactory.createServerSocket(Protocol.VIRTUAL_PORT, CONNECTION_BACKLOG, null); createThread(); } public String getServiceName() { return "bootstrap"; } public synchronized void end(long deadline) { ended = true; try { serverSocket.close(); } catch (IOException e) { // IGNORE } for (ARRG arrg : arrgs.values()) { arrg.end(); } notifyAll(); } synchronized boolean hasEnded() { return ended; } private synchronized ARRG getOrCreateARRG(String poolName) { ARRG result = arrgs.get(poolName); if (result == null) { Statistics statistics = null; if (keepStatistics) { statistics = new Statistics(Protocol.OPCODE_NAMES); statistics.setID("server", poolName); statistics.startWriting(60000); } result = new ARRG(serverSocket.getLocalSocketAddress(), true, new VirtualSocketAddress[0], null, poolName, socketFactory, null); result.start(); arrgs.put(poolName, result); System.out.println("Bootstrap service for new pool: " + poolName); } return result; } private synchronized void cleanup() { // copy values so we can remove them from the map for (ARRG arrg : arrgs.values().toArray(new ARRG[0])) { if (arrg.isDead()) { // this pool is dead, remove it... arrgs.remove(arrg.getPoolName()); } } } private synchronized void createThread() { while (currentNrOfThreads >= MAX_THREADS) { try { wait(); } catch (InterruptedException e) { // IGNORE } } // create new thread for next connection ThreadPool.createNew(this, "bootstrap service connection handler"); currentNrOfThreads++; logger.trace("now " + currentNrOfThreads + " threads"); 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 (hasEnded()) { 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: String poolName = connection.in().readUTF(); ARRG arrg = getOrCreateARRG(poolName); arrg.handleGossip(connection); connection.close(); Statistics statistics = arrg.getStatistics(); if (statistics != null) { statistics.add(opcode, System.currentTimeMillis() - start, connection.read(), connection.written(), true); } break; default: logger.error("unknown opcode: " + opcode); connection.close(); } } catch (IOException e) { if (printErrors) { System.out.println("error on handling connection"); e.printStackTrace(System.out); } connection.close(); } logger.debug("done handling request"); // delete any dead ARRG's cleanup(); threadEnded(); } public String toString() { return "Bootstrap service on virtual port " + Protocol.VIRTUAL_PORT; } public Map<String, String> getStats() { // no statistics return new HashMap<String, String>(); } }