title: What is the state of the art in socket handling with Java 1.7?
body: <p>I'm searching for the best pattern to implement client/server socket communication using appropriate features of Java 1.7 (<em>try-with-resources</em>). It must be assured all threads and resources are closed and released completely. <a href="https://rads.stackoverflow.com/amzn/click/com/0321356683" rel="noreferrer" rel="nofollow noreferrer">Effective Java</a>/<a href="https://rads.stackoverflow.com/amzn/click/com/0132350882" rel="noreferrer" rel="nofollow noreferrer">Clean Code</a> items should be considered as well as simple debugability (only one statement in each line).</p>
  
  <p>Any feedback or discussion would be appreciated.</p>
  
  <h1>First approach</h1>
  
  <pre><code>import java.io.BufferedReader;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.io.OutputStream;
  import java.io.OutputStreamWriter;
  import java.io.PrintWriter;
  import java.net.InetAddress;
  import java.net.ServerSocket;
  import java.net.Socket;
  import java.net.UnknownHostException;
  
  import org.junit.Test;
  
  public final class ThreadedServerTest1 {
      private static final class ThreadedServer implements Runnable {
          private final Socket socket;
  
          ThreadedServer(final Socket socket) {
              this.socket = socket;
          }
  
          public static void main(final String arguments[]) {
              final int port = arguments.length &gt; 0 ? Integer.parseInt(arguments[0]) : 9999;
              try (final ServerSocket serverSocket = new ServerSocket(port)) {
                  while (true) {
                      final Socket socket = serverSocket.accept();
                      final ThreadedServer server = new ThreadedServer(socket);
                      final Thread thread = new Thread(server);
                      thread.start();
                  }
              } catch (final IOException e) {
                  e.printStackTrace();
              }
          }
  
          @Override
          public void run() {
              // @formatter:off
              try (
                  // See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7027552 and
                  // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7013420:
                  final Socket socket = this.socket;
                  final InputStream inputStream = socket.getInputStream();
                  final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                  final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
                  final OutputStream outputStream = socket.getOutputStream();
                  final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
                  final PrintWriter printWriter = new PrintWriter(outputStreamWriter);
              ) {
                  // @formatter:on
                  final String request = bufferedReader.readLine();
                  System.out.println(String.format("Received from client: %s", request));
                  printWriter.println("Hello client!");
                  printWriter.flush();
              } catch (final Exception e) {
                  e.printStackTrace();
              }
          }
      }
  
      private static final class Client {
          public static void main(final String arguments[]) {
              final int port = arguments.length &gt; 0 ? Integer.parseInt(arguments[0]) : 9999;
              try {
                  final InetAddress loopbackAddress = InetAddress.getByName(null);
                  // @formatter:off
                  try (
                      final Socket socket = new Socket(loopbackAddress, port);
                      final OutputStream outputStream = socket.getOutputStream();
                      final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
                      final PrintWriter printWriter = new PrintWriter(outputStreamWriter);
                      final InputStream inputStream = socket.getInputStream();
                      final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                      final BufferedReader bufferedReader = new BufferedReader(inputStreamReader)
                  ) {
                      // @formatter:on
                      printWriter.println("Hello server!");
                      printWriter.flush();
                      final String answer = bufferedReader.readLine();
                      System.out.println(String.format("Answer from server: %s", answer));
                  } catch (final IOException e) {
                      e.printStackTrace();
                  }
              } catch (final UnknownHostException e) {
                  e.printStackTrace();
              }
          }
      }
  
      @Test
      public void test() {
          final String[] arguments = new String[] { "9999" };
          final Thread tread = new Thread() {
              @Override
              public void run() {
                  ThreadedServer.main(arguments);
              };
          };
          tread.start();
          Client.main(arguments);
      }
  }
  </code></pre>
  
  <p>Pros:</p>
  
  <ul>
  <li>Less lines of code</li>
  </ul>
  
  <p>Cons:</p>
  
  <ul>
  <li>Large try-with-resources blocks (no support in Eclipse formatter)</li>
  <li>Second <code>Socke</code>t instance needed to use try-with-resources</li>
  <li>Deep nesting because of the <code>UnknownHostException</code> from <code>InetAddress#getByName(null)</code></li>
  </ul>
  
  <h1>Second approach</h1>
  
  <pre><code>import java.io.BufferedReader;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.io.OutputStream;
  import java.io.OutputStreamWriter;
  import java.io.PrintWriter;
  import java.net.InetAddress;
  import java.net.ServerSocket;
  import java.net.Socket;
  
  import org.junit.Test;
  
  public final class ThreadedServerTest2 {
      private static final class Connection implements AutoCloseable {
          final Socket socket;
          final InputStream inputStream;
          final InputStreamReader inputStreamReader;
          final BufferedReader bufferedReader;
          final OutputStream outputStream;
          final OutputStreamWriter outputStreamWriter;
          final PrintWriter printWriter;
  
          private Connection(final Socket socket) throws IOException {
              this.socket = socket;
              inputStream = socket.getInputStream();
              inputStreamReader = new InputStreamReader(inputStream);
              bufferedReader = new BufferedReader(inputStreamReader);
              outputStream = socket.getOutputStream();
              outputStreamWriter = new OutputStreamWriter(outputStream);
              printWriter = new PrintWriter(outputStreamWriter);
          }
  
          static Connection serverConnection(final Socket socket) throws IOException {
              return new Connection(socket);
          }
  
          static Connection clientConnection(final String hostname, final int port) throws IOException {
              final InetAddress inetAddress = InetAddress.getByName(hostname);
              final Socket socket = new Socket(inetAddress, port);
              return new Connection(socket);
          }
  
          static Connection localhostCientConnection(final int port) throws IOException {
              return clientConnection(null, port);
          }
  
          @Override
          public void close() {
              closeAndIgnoreException(printWriter, outputStreamWriter, outputStream, bufferedReader, inputStreamReader, inputStream, socket);
          }
  
          private void closeAndIgnoreException(final AutoCloseable... closeables) {
              for (final AutoCloseable closeable : closeables) {
                  try {
                      closeable.close();
                  } catch (final Exception ignore) {
                  }
              }
          }
      }
  
      private static final class ThreadedServer implements Runnable {
          private final Socket socket;
  
          private ThreadedServer(final Socket socket) {
              this.socket = socket;
          }
  
          public static void main(final String arguments[]) {
              final int port = arguments.length &gt; 0 ? Integer.parseInt(arguments[0]) : 9999;
              try (final ServerSocket serverSocket = new ServerSocket(port)) {
                  while (true) {
                      final Socket socket = serverSocket.accept();
                      final ThreadedServer server = new ThreadedServer(socket);
                      final Thread thread = new Thread(server);
                      thread.start();
                  }
              } catch (final IOException e) {
                  e.printStackTrace();
              }
          }
  
          @Override
          public void run() {
              try (Connection connection = Connection.serverConnection(socket)) {
                  final String request = connection.bufferedReader.readLine();
                  System.out.println(String.format("Received from client: %s", request));
                  connection.printWriter.println("Hello client!");
                  connection.printWriter.flush();
              } catch (final Exception e) {
                  e.printStackTrace();
              }
          }
      }
  
      private static final class Client {
          public static void main(final String arguments[]) {
              final int port = arguments.length &gt; 0 ? Integer.parseInt(arguments[0]) : 9999;
              try (Connection connection = Connection.localhostCientConnection(port)) {
                  connection.printWriter.println("Hello server!");
                  connection.printWriter.flush();
                  final String answer = connection.bufferedReader.readLine();
                  System.out.println(String.format("Answer from server: %s", answer));
              } catch (final IOException e) {
                  e.printStackTrace();
              }
          }
      }
  
      @Test
      public void test() {
          final String[] arguments = new String[] { "9999" };
          final Thread tread = new Thread() {
              @Override
              public void run() {
                  ThreadedServer.main(arguments);
              };
          };
          tread.start();
          Client.main(arguments);
      }
  }
  </code></pre>
  
  <p>Pros:</p>
  
  <ul>
  <li>Clean and readable try-with-resources blocks</li>
  <li>Support in Eclipse formatter</li>
  </ul>
  
  <p>Cons:</p>
  
  <ul>
  <li>More lines of code</li>
  <li>Own <code>Connection</code> class needed</li>
  <li>Closing the <code>AutoCloseable</code> instances in the <code>Connection</code> class may be forgotten (error prone)</li>
  </ul>
  
  <p>Do you have feedback or additions to the pros and cons? Are there more weaknesses?</p>
  
  <h1>Third approach</h1>
  
  <p>That's the result of the discussion below:</p>
  
  <pre><code>import java.io.BufferedReader;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.io.OutputStreamWriter;
  import java.io.PrintWriter;
  import java.net.InetAddress;
  import java.net.ServerSocket;
  import java.net.Socket;
  
  import org.junit.Test;
  
  public final class ThreadedServerTest3 {
      private static final class ThreadedServer implements Runnable {
          private final Socket socket;
  
          private ThreadedServer(final Socket socket) {
              this.socket = socket;
          }
  
          public static void main(final String arguments[]) {
              final int port = arguments.length &gt; 0 ? Integer.parseInt(arguments[0]) : 9999;
              try (final ServerSocket serverSocket = new ServerSocket(port)) {
                  while (true) {
                      final Socket socket = serverSocket.accept();
                      final ThreadedServer server = new ThreadedServer(socket);
                      final Thread thread = new Thread(server);
                      thread.start();
                  }
              } catch (final IOException e) {
                  e.printStackTrace();
              }
          }
  
          @Override
          public void run() {
              // @formatter:off
              try (
                  // See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7027552 and
                  // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7013420:
                  Socket socket = this.socket;
                  BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                  PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
              ) {
                  // @formatter:on
                  final String request = bufferedReader.readLine();
                  System.out.println(String.format("Received from client: %s", request));
                  printWriter.println("Hello client!");
                  printWriter.flush();
              } catch (final Exception e) {
                  e.printStackTrace();
              }
          }
      }
  
      private static final class Client {
          public static void main(final String arguments[]) {
              final int port = arguments.length &gt; 0 ? Integer.parseInt(arguments[0]) : 9999;
              // @formatter:off
              try (
                  Socket socket = new Socket(InetAddress.getByName(null), port);
                  PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
                  BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()))
              ) {
                  // @formatter:on
                  printWriter.println("Hello server!");
                  printWriter.flush();
                  final String answer = bufferedReader.readLine();
                  System.out.println(String.format("Answer from server: %s", answer));
              } catch (final IOException e) {
                  e.printStackTrace();
              }
          }
      }
  
      @Test
      public void test() {
          final String[] arguments = new String[] { "9999" };
          final Thread tread = new Thread() {
              @Override
              public void run() {
                  ThreadedServer.main(arguments);
              };
          };
          tread.start();
          Client.main(arguments);
      }
  }
  </code></pre>
  
  <p>Pros:</p>
  
  <ul>
  <li>Less lines of code</li>
  <li>No to complex and mostly readable try-with-resources blocks</li>
  <li>No ingored exceptions</li>
  </ul>
  
  <p>Cons:</p>
  
  <ul>
  <li>No support in Eclipse formatter</li>
  <li>Some violations of the rule "only one statements in each line" (simple debugability)</li>
  </ul>