/** * Copyright (C) 2002-2012 The FreeCol Team * * This file is part of FreeCol. * * FreeCol is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * FreeCol is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with FreeCol. If not, see <http://www.gnu.org/licenses/>. */ package net.sf.freecol.metaserver; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Iterator; import java.util.Timer; import java.util.TimerTask; import java.util.logging.Level; import java.util.logging.Logger; import net.sf.freecol.FreeCol; import net.sf.freecol.common.networking.Connection; /** * The entry point and main controller object for the meta server. * * <br> * <br> * * When a new client connects to the meta server a new {@link Connection} is * made, with {@link NetworkHandler} as the control object. * * @see net.sf.freecol.common.networking */ public final class MetaServer extends Thread { private static Logger logger = Logger.getLogger(MetaServer.class.getName()); private static final int REMOVE_DEAD_SERVERS_INTERVAL = 120000; public static final int REMOVE_OLDER_THAN = 90000; /** The public "well-known" socket to which clients may connect. */ private ServerSocket serverSocket; /** A hash of Connection objects, keyed by the Socket they relate to. */ private HashMap<Socket, Connection> connections = new HashMap<Socket, Connection>(); /** * Whether to keep running the main loop that is awaiting new client * connections. */ private boolean running = true; /** The TCP port that is beeing used for the public socket. */ private int port; private NetworkHandler networkHandler; /** * Creates and starts a new <code>MetaServer</code>. * * @param args The command-line options. */ public static void main(String[] args) { int port = -1; try { port = Integer.parseInt(args[0]); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Usage: java net.sf.freecol.metaserver.MetaServer PORT_NUMBER"); System.exit(-1); } catch (NumberFormatException e) { System.out.println("Usage: java net.sf.freecol.metaserver.MetaServer PORT_NUMBER"); System.exit(-1); } MetaServer metaServer = null; try { metaServer = new MetaServer(port); } catch (IOException e) { logger.warning("Could not create MetaServer!"); System.exit(-1); } metaServer.start(); } /** * Creates a new network server. Use {@link #run metaServer.start()} to * start listening for new connections. * * @param port The TCP port to use for the public socket. * @throws IOException if the public socket cannot be created. */ public MetaServer(int port) throws IOException { this.port = port; final MetaRegister mr = new MetaRegister(); networkHandler = new NetworkHandler(this, mr); serverSocket = new ServerSocket(port); Timer t = new Timer(true); t.scheduleAtFixedRate(new TimerTask() { public void run() { try { mr.removeDeadServers(); } catch (Exception ex) { logger.warning("Exception: " + ex.getMessage()); } } }, REMOVE_DEAD_SERVERS_INTERVAL, REMOVE_DEAD_SERVERS_INTERVAL); } /** * Starts the thread's processing. Contains the loop that is waiting for new * connections to the public socket. When a new client connects to the * server a new {@link Connection} is made, with {@link NetworkHandler} as * the control object. */ public void run() { while (running) { Socket clientSocket = null; try { clientSocket = serverSocket.accept(); logger.info("Client connection from: " + clientSocket.getInetAddress().toString()); Connection connection = new Connection(clientSocket, getNetworkHandler(), FreeCol.METASERVER_THREAD); connections.put(clientSocket, connection); } catch (IOException e) { logger.log(Level.WARNING, "Meta-run", e); } } } /** * Gets the control object that handles the network requests. * * @return The <code>NetworkHandler</code>. */ public NetworkHandler getNetworkHandler() { return networkHandler; } /** * Gets the TCP port that is beeing used for the public socket. * * @return The TCP port. */ public int getPort() { return port; } /** * Gets an iterator of every connection to this server. * * @return The <code>Iterator</code>. * @see Connection */ public Iterator<Connection> getConnectionIterator() { return connections.values().iterator(); } /** * Shuts down the server thread. */ public void shutdown() { running = false; try { serverSocket.close(); } catch (IOException e) { logger.log(Level.WARNING, "Could not close the server socket!", e); } Connection c; while ((c = connections.remove(0)) != null) c.close(); logger.info("Server shutdown."); } /** * Gets a <code>Connection</code> identified by a <code>Socket</code>. * * @param socket The <code>Socket</code> that identifies the * <code>Connection</code> * @return The <code>Connection</code>. */ public Connection getConnection(Socket socket) { return connections.get(socket); } /** * Removes the given connection. * * @param connection The connection that should be removed. */ public void removeConnection(Connection connection) { connections.remove(connection.getSocket()); } }