/n-utils/src/main/java/com/noahsloan/nutils/net/SocketListener.java
http://n-utils.googlecode.com/ · Java · 213 lines · 89 code · 19 blank · 105 comment · 6 complexity · ef4bdd55a089d843ff5553ffcc2c21a8 MD5 · raw file
- package com.noahsloan.nutils.net;
-
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.net.SocketAddress;
-
- import com.noahsloan.nutils.log.Logger;
-
- /**
- * Base class for classes that listen on a socket. A new instance can function
- * as a server by calling run(), although this class is designed to be part of a
- * larger server.
- *
- * All responsibility for handling a new connection is handled by
- * handleConnection(Socket).
- *
- * Implements Runnable so that the listener may be given to a thread pool.
- *
- * In addition, several methods other than handleConnection(Socket) may be
- * overridden by sub-classes, although they do not have to be.
- *
- * @see SocketListener#bindFailed()
- * @see SocketListener#handleSocketError(Socket, IOException)
- * @see SocketListener#handleSSError(ServerSocket, IOException)
- *
- * These methods may not throw any checked exceptions, and should not throw
- * RuntimeExceptions. A general strategy for subclasses might be to utilize
- * these methods as listeners, to notify another thread of this server's
- * failure.
- *
- * @author Noah Sloan
- * @see SocketListener#handleConnection(Socket)
- */
- public abstract class SocketListener implements Runnable {
-
- private static final Logger LOG = Logger.getLogger(SocketListener.class);
-
- /**
- * the number of times in a row to try to restart the server socket
- */
- public static final int MAX_ATTEMPTS = Integer.getInteger(
- "n-utils.net.SocketListener.max-attempts", 15);
-
- private volatile boolean shutdown; // is this listener shutdown?
-
- private volatile ServerSocket ss;
-
- protected final SocketAddress address; // the socket address to bind to
-
- /**
- * Create a listener for the wildcard address and given port.
- *
- * @param port
- */
- public SocketListener(int port) {
- this(new InetSocketAddress(port));
- }
-
- /**
- * Create a listener for address:port
- *
- * @param address
- * @param port
- */
- public SocketListener(String address, int port) {
- this(new InetSocketAddress(address, port));
- }
-
- /**
- * Create a listener on the given socket address.
- *
- * @param address
- */
- public SocketListener(SocketAddress address) {
- this.address = address;
- }
-
- /**
- * Creates a server socket that listens on the port provided in the
- * constructor. Will attempt to re-bind to the given socket address up to
- * MAX_ATTEMPTS times.
- *
- * @see SocketListener#MAX_ATTEMPTS
- */
- public void run() {
- boolean canBind = true;
- int bindAttempts = 0;
- while (!shutdown && canBind) {
- LOG.info("Starting Server Socket. Binding to " + address);
- // Assume that we cannot bind until we successfully get a
- // connection from a client
- canBind = false;
- try {
- // create a new socket and bind to it
- ss = new ServerSocket();
- ss.setReuseAddress(true);
- ss.bind(address);
-
- // as long as we are bound, and not shutdown, handle
- // connections
- while (!shutdown && ss.isBound()) {
- Socket socket = ss.accept();
- canBind = true;// we have successfully made a connection
- try {
- handleConnection(socket);
- } catch (IOException e) {
- handleSocketError(socket, e);
- }
- }
- } catch (IOException e) {
- handleSSError(ss, e);
- }
- // reset bind attempts if we successfully bound, otherwise
- // count the
- // attempts
- bindAttempts = canBind ? 0 : bindAttempts + 1;
- // shutdown if we fail to bind too many times
- shutdown = shutdown || bindAttempts > MAX_ATTEMPTS;
- }
- // print an error message if this shutdown was because of a
- // failure to bind
- if (bindAttempts > MAX_ATTEMPTS) {
- bindFailed();
- }
- }
-
- /**
- * Prints an error message and attempts to close the ServerSocket. May be
- * overridden by subclasses but may not throw any new checked exceptions.
- *
- * @param ss
- * the ServerSocket that threw the exception.
- * @param e
- * the exception thrown.
- */
- protected void handleSSError(ServerSocket ss, IOException e) {
- LOG.info("Server Socket Error");
- try {
- ss.close();
- } catch (IOException e1) {
- // if it wont close, we can't do anything about it
- LOG.error("Error closing socket", e1);
- }
- }
-
- /**
- * Prints an error message and attempts to close the socket. May be
- * overridden by subclasses but may not throw any new checked exceptions.
- *
- * @param socket
- * the socket that threw the exception.
- * @param e
- * the exception thrown.
- */
- protected void handleSocketError(Socket socket, IOException e) {
- LOG.warn(String
- .format("Error on socket to %s", socket.getInetAddress()), e);
- try {
- socket.close();
- } catch (IOException e1) {
- // can't do anything if it wont close...
- LOG.warn("Error closing socket", e1);
- }
- }
-
- /**
- * Logs an error message when the server socket fails to bind. May be
- * overridden by sub-classes, but may not throw any checked exceptions.
- */
- protected void bindFailed() {
- LOG.error(String.format("%s could not (re)bind to %s after"
- + " %d consecutive attempts.\n", this.getClass().getName(),
- address, MAX_ATTEMPTS));
- }
-
- /**
- * Handle is expected to start a new thread and/or quickly return so it can
- * handle the next incoming connection.
- *
- * @param socket
- * the socket that just connected.
- * @throws IOException
- * if there is an error on the socket.
- */
- protected abstract void handleConnection(Socket socket) throws IOException;
-
- /**
- * Shutdown this listener. Note that this will not stop any servant threads
- * you may have spawned, it is up to you to ensure that they terminate
- * reasonably.
- *
- * @throws IOException
- * if an error occurs closing the socket.
- *
- */
- public void shutdown() throws IOException {
- shutdown = true;
- ss.close();
- }
-
- public static void main(String[] args) {
- new SocketListener(Integer.getInteger("socketListenerPort", 1029)) {
- @Override
- // dumps data to std out
- protected void handleConnection(Socket socket) throws IOException {
- new com.noahsloan.nutils.InToOut(socket.getInputStream(), System.out,
- true, false).connect();
- }
- }.run();
- }
- }