fitnesse.FitNesseExpediter.java Source code

Java tutorial

Introduction

Here is the source code for fitnesse.FitNesseExpediter.java

Source

// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved.
// Released under the terms of the CPL Common Public License version 1.0.
package fitnesse;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.GregorianCalendar;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;

import fitnesse.components.LogData;
import fitnesse.http.HttpException;
import fitnesse.http.Request;
import fitnesse.http.Response;
import fitnesse.http.ResponseSender;
import fitnesse.http.SimpleResponse;
import fitnesse.responders.ErrorResponder;
import org.apache.commons.lang.StringUtils;

public class FitNesseExpediter implements ResponseSender, Runnable {
    private static final Logger LOG = Logger.getLogger(FitNesseExpediter.class.getName());

    private final Socket socket;
    private final InputStream input;
    private final OutputStream output;
    private final FitNesseContext context;
    private final ExecutorService executorService;
    private final long requestParsingTimeLimit;
    private Request request;
    private Response response;

    public FitNesseExpediter(Socket socket, FitNesseContext context, ExecutorService executorService)
            throws IOException {
        this(socket, context, executorService, 10000);
    }

    public FitNesseExpediter(Socket socket, FitNesseContext context, ExecutorService executorService,
            long requestParsingTimeLimit) throws IOException {
        this.context = context;
        this.socket = socket;
        this.executorService = executorService;
        input = socket.getInputStream();
        output = socket.getOutputStream();
        this.requestParsingTimeLimit = requestParsingTimeLimit;
    }

    @Override
    public void run() {
        try {
            // Storing them in instance fields, since we need info for logging when the connection is closed.
            request = makeRequest();
            response = makeResponse(request);
            sendResponse(response);
        } catch (SocketException se) {
            // can be thrown by makeResponse or sendResponse.
        } catch (Throwable e) { // NOSONAR
            // This catch is intentional, since it's the last point where we can catch exceptions that occur in this thread.
            LOG.log(Level.WARNING, "Unexpected exception", e);
        }
    }

    @Override
    public void send(byte[] bytes) throws IOException {
        output.write(bytes);
        output.flush();
    }

    @Override
    public void close() {
        log(socket, request, response);
        if (!socket.isClosed()) {
            try {
                socket.close();
            } catch (IOException e) {
                LOG.log(Level.WARNING, "Error while closing socket", e);
            }
        }
    }

    private Request makeRequest() {
        Request request = new Request(input);
        request.setContextRoot(context.contextRoot);
        return request;
    }

    private void sendResponse(Response response) throws IOException {
        response.sendTo(this);
    }

    private Response makeResponse(final Request request) throws Exception {
        Response response;
        try {
            executorService.submit(new Callable<Request>() {
                @Override
                public Request call() throws Exception {
                    request.parse();
                    return request;
                }
            }).get(requestParsingTimeLimit, TimeUnit.MILLISECONDS);

            if (request.hasBeenParsed()) {
                if (context.contextRoot.equals(request.getRequestUri() + "/")) {
                    response = new SimpleResponse();
                    response.redirect(context.contextRoot, "");
                } else {
                    response = createGoodResponse(request);
                }
            } else {
                response = reportError(request, 400, "The request could not be parsed.");
            }
        } catch (SocketException se) {
            throw se;
        } catch (TimeoutException e) {
            String message = "The client request has been unproductive for too long. It has timed out and will no longer be processed.";
            LOG.log(Level.FINE, message, e);
            response = reportError(request, 408, message);
        } catch (HttpException e) {
            LOG.log(Level.FINE, "An error occured while fulfilling user request", e);
            response = reportError(request, 400, e.getMessage());
        } catch (Exception e) {
            LOG.log(Level.WARNING, "An error occured while fulfilling user request", e);
            response = reportError(request, e);
        }

        // Add those as default headers?
        response.addHeader("Server", "FitNesse-" + context.version);
        response.addHeader("Connection", "close");
        return response;
    }

    public Response createGoodResponse(Request request) throws Exception {
        if (StringUtils.isBlank(request.getResource()) && StringUtils.isBlank(request.getQueryString()))
            request.setResource("FrontPage");
        Responder responder = context.responderFactory.makeResponder(request);
        responder = context.authenticator.authenticate(context, request, responder);
        return responder.makeResponse(context, request);
    }

    private Response reportError(Request request, int status, String message) throws Exception {
        return new ErrorResponder(message, status).makeResponse(context, request);
    }

    private Response reportError(Request request, Exception e) throws Exception {
        return new ErrorResponder(e).makeResponse(context, request);
    }

    public static LogData makeLogData(Socket socket, Request request, Response response) {
        return new LogData(((InetSocketAddress) socket.getRemoteSocketAddress()).getAddress().getHostAddress(),
                new GregorianCalendar(), request.getRequestLine(), response.getStatus(), response.getContentSize(),
                request.getAuthorizationUsername());
    }

    public void log(Socket s, Request request, Response response) {
        if (context.logger != null)
            context.logger.log(makeLogData(s, request, response));
    }
}