package nl.hsac.fitnesse.fixture.util;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import org.apache.http.entity.ContentType;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Receiver for a callback from application being tested.
 */
public class HttpServer <T extends HttpResponse> {
    private static final ContentType XML_UTF8_TYPE = ContentType.parse(XmlHttpResponse.CONTENT_TYPE_XML_TEXT_UTF8);

    private final T response;
    private final com.sun.net.httpserver.HttpServer server;
    private final AtomicInteger requestsReceived = new AtomicInteger(0);
    private final Object lock = new Object();

    /**
     * Creates new.
     * @param aPath context the server will serve (must start with '/').
     * @param aResponse response to send when request is received, request will
     *                  be added to it when this server receives one.
     */
    public HttpServer(String aPath, T aResponse) {
        response = aResponse;
        try {
            server = com.sun.net.httpserver.HttpServer.create();
            bind(server);
            server.createContext(aPath, getHandler(aResponse));
            server.start();
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * @return address the server listens on.
     */
    public InetSocketAddress getAddress() {
        return server.getAddress();
    }

    /**
     * @return response, with request filled (with last received request) if getRequestsReceived() is larger than 0.
     */
    public T getResponse() {
        return response;
    }

    /**
     * @return number of requests this server received.
     */
    public int getRequestsReceived() {
        return requestsReceived.get();
    }

    /**
     * @param maxWait ms to wait at most.
     * @return response with last request filled, if at least one was received.
     */
    public T waitForRequest(long maxWait) {
        long start = System.currentTimeMillis();
        try {
            while (requestsReceived.get() < 1
                    && (System.currentTimeMillis() - start) < maxWait) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException ex) {
                    throw new RuntimeException(ex);
                }
            }
        } finally {
            stopServer();
        }

        return getResponse();
    }

    /**
     * Stops server from listening.
     */
    public void stopServer() {
        synchronized (lock) {
            server.stop(0);
        }
    }

    private void bind(com.sun.net.httpserver.HttpServer server) {
        try {
            InetAddress address = InetAddress.getLocalHost();
            int port = getFreePort();
            InetSocketAddress isa = new InetSocketAddress(address, port);
            server.bind(isa, 1);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Finds free port number.
     * @return first available port number above 8000.
     */
    protected int getFreePort() {
        int port = 0;
        for (int possiblePort = 8000; port == 0; possiblePort++) {
            ServerSocket s = null;
            try {
                s = new ServerSocket(possiblePort);
                port = s.getLocalPort();
            } catch (IOException e) {
                // try next number
                continue;
            } finally {
                if (s != null) {
                    try {
                        s.close();
                    } catch (IOException e) {
                        // why would this happen?
                        throw new IllegalStateException("Unable to close port: " + possiblePort, e);
                    }
                }
            }
        }
        return port;
    }

    protected HttpHandler getHandler(final T aResponse) {
        HttpHandler result = new HttpHandler() {
            @Override
            public void handle(HttpExchange he) throws IOException {
            // ensure we never handle multiple requests at the same time
            synchronized (lock) {
                OutputStream os = null;
                try {
                    String request;
                    if ("POST".equals(he.getRequestMethod()) || "PUT".equals(he.getRequestMethod())) {
                        InputStream is = he.getRequestBody();
                        request = FileUtil.streamToString(is, String.format("http %s request", he.getRequestMethod()));
                    } else {
                        request = String.format("%s: %s", he.getRequestMethod(), he.getRequestURI().toString());
                    }
                    aResponse.setRequest(request);

                    ContentType contentType = XML_UTF8_TYPE;
                    byte[] responseBytes = aResponse.getResponse()
                                            .getBytes(contentType.getCharset());
                    he.sendResponseHeaders(aResponse.getStatusCode(),
                                            responseBytes.length);
                    he.getResponseHeaders()
                            .add("Content-Type", contentType.toString());
                    os = he.getResponseBody();
                    os.write(responseBytes);
                    os.flush();
                } finally {
                    incrementRequestsReceived();
                    if (os != null) {
                        os.close();
                    }
                }
            }
            }
        };
        return result;
    }

    protected int incrementRequestsReceived() {
        return requestsReceived.incrementAndGet();
    }
}

@@ -317,38 +317,8 @@
 ss;%0A
-import java.net.ServerSocket;%0A
 impo
@@ -2757,23 +2757,99 @@
  %7D%0A%0A    
-private
+/**%0A     * Finds free port number and binds the server to it.%0A     */%0A    protected
  void bi
@@ -2901,32 +2901,58 @@
 %7B%0A        try %7B%0A
+            int port = 0;%0A
             Inet
@@ -3013,217 +3013,88 @@
     
+for (
 int po
-rt = getFreePort();%0A            InetSocketAddress isa = new InetSocketAddress(address, port);%0A            server.bind(isa, 1);%0A        %7D catch (Exception ex) %7B%0A            throw new RuntimeException(ex);
+ssiblePort = 8000; port == 0; possiblePort++) %7B%0A                try %7B
 %0A   
@@ -3102,242 +3102,96 @@
     
-%7D%0A
     
-%7D%0A%0A
     
-/**%0A     * Finds free port number.%0A     * @return first available port number above 8000.%0A     */%0A    protected int getFreePort() %7B%0A        int port = 0;%0A        for (int possiblePort = 8000; port == 0; possiblePort++) %7B%0A
+    InetSocketAddress s = new InetSocketAddress(address, possiblePort);%0A        
     
@@ -3202,29 +3202,25 @@
     
-S
+s
 erver
-Socket s = null
+.bind(s, 1)
 ;%0A  
@@ -3233,51 +3233,23 @@
     
-try %7B%0A
+ 
        
-         s = new ServerSocket(
+port = 
 poss
@@ -3260,52 +3260,14 @@
 Port
-)
 ;%0A    
-            port = s.getLocalPort();%0A
     
@@ -3316,16 +3316,20 @@
         
+    
 // try n
@@ -3339,16 +3339,20 @@
  number%0A
+    
         
@@ -3385,126 +3385,28 @@
     
-%7D finally %7B%0A                if (s != null) %7B%0A                    try %7B%0A                        s.close();%0A            
+    %7D%0A            %7D%0A
     
@@ -3410,34 +3410,32 @@
        %7D catch (
-IO
 Exception e) %7B%0A 
@@ -3421,32 +3421,33 @@
 tch (Exception e
+x
 ) %7B%0A            
@@ -3450,231 +3450,49 @@
     
-            // why would this happen?%0A                        throw new IllegalStateException(%22Unable to close port: %22 + possiblePort, e);%0A                    %7D%0A                %7D%0A            %7D%0A        %7D%0A        return port;
+throw new RuntimeException(ex);%0A        %7D
 %0A   

Fix finding of free port. It turns out the actual check whether the port is free does not occur until the server binds (i.e. not on ServerSocket creation)