package ibis.ipl.server; import ibis.ipl.impl.IbisIdentifier; import ibis.ipl.support.Connection; import ibis.ipl.support.management.AttributeDescription; import ibis.smartsockets.virtual.VirtualServerSocket; import ibis.smartsockets.virtual.VirtualSocketFactory; import ibis.util.ThreadPool; import java.io.IOException; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class ServerConnectionHandler implements Runnable { private static final int CONNECTION_BACKLOG = 50; static final int MAX_THREADS = 10; private static final Logger logger = LoggerFactory .getLogger(ServerConnectionHandler.class); private final Server server; private final VirtualServerSocket serverSocket; private int currentNrOfThreads = 0; private boolean ended = false; public ServerConnectionHandler(Server server, VirtualSocketFactory socketFactory) throws IOException { this.server = server; serverSocket = socketFactory.createServerSocket(ServerConnectionProtocol.VIRTUAL_PORT, CONNECTION_BACKLOG, null); createThread(); } private synchronized void createThread() { while (currentNrOfThreads >= MAX_THREADS && !ended) { try { wait(); } catch (InterruptedException e) { // IGNORE } } if (ended) { return; } // create new thread for next connection ThreadPool.createNew(this, "server connection handler"); currentNrOfThreads++; logger.debug("Now " + currentNrOfThreads + " connections"); } private synchronized void threadEnded() { currentNrOfThreads--; notifyAll(); } private synchronized boolean hasEnded() { return ended; } private void handleGetAddress(Connection connection) throws IOException { connection.sendOKReply(); connection.out().writeUTF(server.getAddress()); } private void handleGetServiceNames(Connection connection) throws IOException { String[] result = server.getServiceNames(); connection.sendOKReply(); connection.out().writeInt(result.length); for (String name : result) { connection.out().writeUTF(name); } } private void handleGetHubs(Connection connection) throws IOException { String[] result = server.getHubs(); connection.sendOKReply(); connection.out().writeInt(result.length); for (String hub : result) { connection.out().writeUTF(hub); } } private void handleAddHubs(Connection connection) throws IOException { int nrOfNewHubs = connection.in().readInt(); if (nrOfNewHubs < 0) { throw new IOException("negative hub list size"); } String[] hubs = new String[nrOfNewHubs]; for (int i = 0; i < hubs.length; i++) { hubs[i] = connection.in().readUTF(); } server.addHubs(hubs); connection.sendOKReply(); } private void handleEnd(Connection connection) throws IOException { long timeout = connection.in().readLong(); // send reply and close first. End will cause our // SocketFactory to dissappear connection.sendOKReply(); connection.close(); server.end(timeout); } private void handleRegistryGetPools(Connection connection) throws IOException { String[] result = server.getRegistryService().getPools(); connection.sendOKReply(); connection.out().writeInt(result.length); for (String element : result) { connection.out().writeUTF(element); } } private void handleRegistryGetPoolSizes(Connection connection) throws IOException { Map<String, Integer> result = server.getRegistryService().getPoolSizes(); connection.sendOKReply(); connection.out().writeInt(result.size()); for (Map.Entry<String, Integer> entry: result.entrySet()) { connection.out().writeUTF(entry.getKey()); connection.out().writeInt(entry.getValue()); } } private void handleRegistryGetLocations(Connection connection) throws IOException { String poolName = connection.in().readUTF(); String[] result = server.getRegistryService().getLocations(poolName); connection.sendOKReply(); connection.out().writeInt(result.length); for (String element : result) { connection.out().writeUTF(element); } } private void handleRegistryGetMembers(Connection connection) throws IOException { String pool = connection.in().readUTF(); IbisIdentifier[] result = server.getRegistryService().getMembers(pool); connection.sendOKReply(); connection.out().writeInt(result.length); for (IbisIdentifier element : result) { byte[] bytes = element.toBytes(); connection.out().write(bytes); } } private void handleManagementGetAttributes(Connection connection) throws IOException, ClassNotFoundException { IbisIdentifier ibis = (IbisIdentifier) connection.readObject(); AttributeDescription[] attributes = (AttributeDescription[]) connection .readObject(); Object[] result = server.getManagementService().getAttributes(ibis, attributes); connection.sendOKReply(); connection.writeObject(result); } 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 } } createThread(); if (connection == null) { threadEnded(); return; } byte opcode = 0; try { byte magic = connection.in().readByte(); if (magic != ServerConnectionProtocol.MAGIC_BYTE) { throw new IOException( "Invalid header byte in accepting connection"); } opcode = connection.in().readByte(); logger.debug("got request, opcode = " + opcode); // public static final byte OPCODE_GET_ADDRESS = 0; // public static final byte OPCODE_GET_SERVICE_NAMES = 1; // public static final byte OPCODE_GET_HUBS = 2; // public static final byte OPCODE_ADD_HUBS = 3; // public static final byte OPCODE_END = 4; // public static final byte OPCODE_REGISTRY_GET_MEMBERS = 5; // public static final byte OPCODE_MANAGEMENT_GET_ATTRIBUTES = 6; switch (opcode) { case ServerConnectionProtocol.OPCODE_GET_ADDRESS: handleGetAddress(connection); break; case ServerConnectionProtocol.OPCODE_GET_SERVICE_NAMES: handleGetServiceNames(connection); break; case ServerConnectionProtocol.OPCODE_GET_HUBS: handleGetHubs(connection); break; case ServerConnectionProtocol.OPCODE_ADD_HUBS: handleAddHubs(connection); break; case ServerConnectionProtocol.OPCODE_END: handleEnd(connection); break; case ServerConnectionProtocol.OPCODE_REGISTRY_GET_POOLS: handleRegistryGetPools(connection); break; case ServerConnectionProtocol.OPCODE_REGISTRY_GET_POOL_SIZES: handleRegistryGetPoolSizes(connection); break; case ServerConnectionProtocol.OPCODE_REGISTRY_GET_LOCATIONS: handleRegistryGetLocations(connection); break; case ServerConnectionProtocol.OPCODE_REGISTRY_GET_MEMBERS: handleRegistryGetMembers(connection); break; case ServerConnectionProtocol.OPCODE_MANAGEMENT_GET_ATTRIBUTES: handleManagementGetAttributes(connection); break; default: logger.error("unknown opcode: " + opcode); } } catch (Exception e) { // send error to client connection.closeWithError("Server: " + e.getMessage()); logger.error("error on handling connection", e); } finally { connection.close(); } threadEnded(); } public void end() { synchronized (this) { ended = true; notifyAll(); } try { serverSocket.close(); } catch (Exception e) { // IGNORE } } }