public enum Method {

    GET, POST, PUT, TRACE, OPTIONS, HEAD, DELETE
} public interface RequestHandler {

    RequestHandler with(int statusCode, String contentType, String body);
    
    RequestHandler with(int statusCode, String contentType, Object entity);
    
    RequestHandler with(HttpRequestHandler customHandler);

    RequestHandler withSessionHandler(SessionHandler sessionHandler);
    
    RequestHandler withHeader(String name, String value);
    
    RequestHandler after(long delay, TimeUnit delayUnit);
    
    RequestHandler every(long period, TimeUnit periodUnit);
    
    RequestHandler every(long period, TimeUnit periodUnit, int times);
    
    RequestHandler withTimeout(long timeout, TimeUnit timeoutUnit);
    
    RequestHandler upon(Method method, String resource);
    
    RequestHandler upon(Method method, String resource, String contentType);
    
    RequestHandler withRedirect(String location);
    
    RequestHandler withRedirect(String location, int statusCode);
} public class InterpolatedResponseBody extends StringResponseBody {

    public InterpolatedResponseBody(String body, HttpRequest req) {
        super(ResponseBodyInterpolator.interpolate(body, req));
    }
} public class MarshalledResponseBody extends InputStreamResponseBody {

    public MarshalledResponseBody(Object entity, Marshaller marshaller) {
        
        super(marshaller.marshal(entity));
    }
} public abstract class ResponseBody {

    public abstract void send(Response resp, String contentType);
    
    public abstract void sendAndCommit(Response resp, String contentType);
    
    public abstract boolean hasContent();
    
    protected void addStandardHeaders(Response response, String responseContentType) {
        
        long time = System.currentTimeMillis();
        response.setValue("Content-Type", responseContentType);
        response.setValue("Server", "Fixd/1.0 (Simple 5.1.4)");
        response.setDate("Date", time);
        response.setDate("Last-Modified", time);
    }
} public class FixtureContainer implements Container {
    
    private static final Logger logger = LoggerFactory.getLogger(FixtureContainer.class);
    
    private final RequestResolver requestResolver = new RequestResolver();
    
    private final MarshallerContainer marshallerContainer = new MarshallerContainer();
    
    private final SessionManager sessionManager = new SessionManager();
    
    private final CapturedRequestContainer capturedRequestContainer = new CapturedRequestContainer();
    
    private final AsyncHandler asyncHandler;
    
    public FixtureContainer() {
        asyncHandler = new AsyncHandler(Executors.newCachedThreadPool());
    }
    
    public FixtureContainer(int aysncThreadPoolSize) {
        asyncHandler = new AsyncHandler(Executors.newFixedThreadPool(aysncThreadPoolSize));
    }
    
    public HandlerKey addHandler(RequestHandlerImpl handler, 
            Method method, String resource) {
        
        return addHandler(handler, method, resource, null);
    }
    
    public HandlerKey addHandler(RequestHandlerImpl handler, 
            Method method, String resource, String contentType) {
        return requestResolver.addHandler(handler, method, resource, contentType);
    }
    
    public void addUponHandler(Upon upon) {
        requestResolver.addUponHandler(this, upon);
    }
    
    public Queue<CapturedRequest> getCapturedRequests() {
        return capturedRequestContainer.getCapturedRequests();
    }
    
    public CapturedRequest nextCapturedRequest() {
        return capturedRequestContainer.nextCapturedRequest();
    }
    
    public void setCapturedRequestLimit(int limit) {
        capturedRequestContainer.setCapturedRequestLimit(limit);
    }
    
    public void addContentMarshaller(String contentType, RequestMarshallerImpl marshaller) {
        marshallerContainer.addContentMarshaller(contentType, marshaller);
    }
    
    public void addContentUnmarshaller(String contentType, RequestUnmarshallerImpl unmarshaller) {
        marshallerContainer.addContentUnmarshaller(contentType, unmarshaller);
    }
    
    public void handle(Request request, Response response) {

        try {
            
            SimpleCapturedRequest captured = new SimpleCapturedRequest(request);
            capturedRequestContainer.addCapturedRequest(captured);
            
            String responseContentType = "text/plain";
            ResponseBody responseBody = new StringResponseBody("");
            int handlerStatusCode = Status.OK.code;
            
            ResolvedRequest resolved = requestResolver.resolve(request);
            if (resolved.errorStatus != null) {
                response.setStatus(resolved.errorStatus);
                sendAndCommitResponse(response, responseContentType, responseBody);
                return;
            }
            
            if (requestResolver.requestIsForUponHandler(resolved)) {
                
                asyncHandler.broadcastToSubscribers(request, resolved.route, 
                        requestResolver.getUpon(resolved), captured);
                /* continue handling the request, as it needs to 
                 * return a normal response */
            }
            
            /* create a new session if required */
            SessionHandler sessionHandler = resolved.handler.sessionHandler();
            if (sessionHandler != null) {
                sessionManager.createNewSession(request, response, resolved.route, sessionHandler, 
                        marshallerContainer.newUnmarshallerProvider());
            }
            
            /* set the response body */
            if (!resolved.handler.isSuspend()) {
                Session session = sessionManager.getSessionIfExists(request);
                ResponseBody handlerBody = resolved.handler.body(
                        new SimpleHttpRequest(request, session, resolved.route, 
                                marshallerContainer.newUnmarshallerProvider()), 
                        response, marshallerContainer.newMarshallerProvider());
                if (handlerBody != null && handlerBody.hasContent()) {
                    responseBody = handlerBody;
                }
                handlerStatusCode = resolved.handler.statusCode();
            }
            
            /* set the content type */
            String handlerContentType = resolved.handler.contentType();
            if (handlerContentType != null && 
                    handlerContentType.trim().length() != 0) {
                
                responseContentType = handlerContentType;
            }
            
            /* set the response status code */
            if (handlerStatusCode == -1) {
                throw new RuntimeException("a response status code must be specified");
            }
            response.setCode(handlerStatusCode);
            
            /* set any headers */
            Set<SimpleImmutableEntry<String, String>> headers = 
                    resolved.handler.headers();
            for (SimpleImmutableEntry<String, String> header : headers) {
                response.addValue(header.getKey(), header.getValue());
            }
            
            /* handle the response */
            if (resolved.handler.isAsync()) {
                asyncHandler.doAsync(request, response, resolved.handler, responseContentType, responseBody, 
                        marshallerContainer.newMarshallerProvider(), 
                        marshallerContainer.newUnmarshallerProvider());
            } else {
                sendAndCommitResponse(response, responseContentType, responseBody);
            }
            
        } catch (Throwable e) {
            
            logger.error("internal server error", e);
            response.setStatus(Status.INTERNAL_SERVER_ERROR);
            sendAndCommitResponse(response, "text/plain", new StringResponseBody(""));
        }
    }
    
    public void stop() {
        
        asyncHandler.stop();
    }

    private void sendAndCommitResponse(Response response, 
            String responseContentType, ResponseBody responseBody) {
        responseBody.sendAndCommit(response, responseContentType);
    }
} public interface Marshaller {

    InputStream marshal(Object entity);
} public interface MarshallerProvider {

    /**
     * 
     * Returns the Marshaller for the given content-type,
     * or null if there is no Marshaller for the given
     * content-type.
     * 
     * @param contentType
     * @return the Marshaller for the given content-type, or
     *         null if there is no Marshaller for the given
     *         content-type
     */
    Marshaller getMarshaller(String contentType);
} public interface HttpRequest {

    String getPath();
    
    String getUndecodedPath();
    
    Set<String> getRequestParameterNames();
    
    String getRequestParameter(String name);
    
    String getPathParameter(String name);
    
    List<String> getHeaderNames();
    
    String getHeaderValue(String name);
    
    String getBody();
    
    InputStream getBodyAsStream() throws IOException;
    
    <T> T getBody(Class<T> type);
    
    long getContentLength();
    
    String getContentType();
    
    String getMethod();
    
    long getTime();
    
    String getQuery();
    
    int getMajor();
    
    int getMinor();
    
    String getTarget();
    
    /**
     * @return a session, if it exists, or null if it does not
     */
    Session getSession();
    
    Route getRoute();
} public interface HttpRequestHandler {

    void handle(HttpRequest request, HttpResponse response);
 } public class SimpleHttpResponse implements HttpResponse {

    private final HttpRequest request;
    
    private final Response response;
    
    private ResponseBody body;
    private Object entity;
    private String contentType;
    private int statusCode;
    
    public SimpleHttpResponse(HttpRequest req, Response response) {
        this.request = req;
        this.response = response;
    }
    
    public void setBody(InputStream in) {
        this.body = new InputStreamResponseBody(in);
    }

    public void setBody(byte[] content) {
        this.body = new ByteArrayResponseBody(content);
    }

    public void setBody(String content) {
        this.body = new StringResponseBody(content);
    }
    
    public void setBody(Object entity) {
        this.entity = entity;
    }

    public void setInterpolatedBody(String content) {
        this.body = new InterpolatedResponseBody(content, request);
    }
    
    public ResponseBody getBody(MarshallerProvider marshallerProvider) {
        
        if (entity != null) {
            if (contentType == null || contentType.trim().length() == 0) {
                throw new RuntimeException("an entity has been specified " +
                		"in the response body, but no content type has " +
                		"been specified");
            }
            Marshaller marshaller = marshallerProvider.getMarshaller(contentType);
            if (marshaller == null) {
                throw new RuntimeException("an entity has been specified " +
                        "in the response body, but no marshaller exists for " +
                        "content type: " + contentType);
            }
            this.body = new MarshalledResponseBody(entity, marshaller);
        }
        return body;
    }
    
    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

    public String getContentType() {
        return contentType;
    }
    
    public void setStatusCode(int statusCode) {
        this.statusCode = statusCode;
    }

    public int getStatusCode() {
        return statusCode;
    }

    public void setCookie(String name, String value) {

        Cookie cookie = new Cookie(name, value);
        response.setCookie(cookie);
    }

    public void addHeader(String name, String value) {

        response.addValue(name, value);
    }
    
    public void redirect(String location) {

        redirect(location, Status.FOUND.code);
    }
    
    public void redirect(String location, int statusCode) {
        
        addHeader("Location", location);
        addHeader("Connection", "close");
        setStatusCode(statusCode);
        setContentType("text/plain");
        setBody("");
    }
} public interface SessionHandler {

    void onCreate(HttpRequest request);
}
import org.bigtesting.fixd.response.impl.SimpleHttpResponse;
import org.bigtesting.fixd.session.SessionHandler;
import org.simpleframework.http.Response;
import org.simpleframework.http.Status;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.bigtesting.fixd.Method;
import org.bigtesting.fixd.RequestHandler;
import org.bigtesting.fixd.core.body.InterpolatedResponseBody;
import org.bigtesting.fixd.core.body.MarshalledResponseBody;
import org.bigtesting.fixd.core.body.ResponseBody;
import org.bigtesting.fixd.core.container.FixtureContainer;
import org.bigtesting.fixd.marshalling.Marshaller;
import org.bigtesting.fixd.marshalling.MarshallerProvider;
import org.bigtesting.fixd.request.HttpRequest;
import org.bigtesting.fixd.request.HttpRequestHandler;
/*
 * Copyright (C) 2015 BigTesting.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.bigtesting.fixd.core;



/**
 * 
 * @author Luis Antunes
 */
public class RequestHandlerImpl implements RequestHandler {

    private int statusCode = -1;
    private String contentType;
    private String body;
    private SessionHandler sessionHandler;
    private long delay = -1;
    private TimeUnit delayUnit;
    private long period = -1;
    private TimeUnit periodUnit;
    private int periodTimes = -1;
    private long timeout = -1;
    private TimeUnit timeoutUnit;
    private Upon upon;
    private Set<SimpleImmutableEntry<String, String>> headers = 
            new HashSet<SimpleImmutableEntry<String,String>>();
    
    private Object entity;
    
    private HttpRequestHandler httpHandler;
    
    private final FixtureContainer container;
    
    public RequestHandlerImpl(FixtureContainer container) {
        this.container = container;
    }
    
    public RequestHandler with(int statusCode, String contentType, String body) {
        
        this.statusCode = statusCode;
        this.contentType = contentType;
        this.body = body;
        return this;
    }
    
    public RequestHandler with(int statusCode, String contentType, Object entity) {
        
        this.statusCode = statusCode;
        this.contentType = contentType;
        this.entity = entity;
        return this;
    }
    
    public RequestHandler with(HttpRequestHandler customHandler) {
        
        this.httpHandler = customHandler;
        return this;
    }

    public RequestHandler withSessionHandler(SessionHandler sessionHandler) {
        
        this.sessionHandler = sessionHandler;
        return this;
    }
    
    public RequestHandler withHeader(String name, String value) {
        
        headers.add(new SimpleImmutableEntry<String, String>(name, value));
        return this;
    }
    
    public RequestHandler after(long delay, TimeUnit delayUnit) {
        
        this.delay = delay;
        this.delayUnit = delayUnit;
        return this;
    }
    
    public RequestHandler every(long period, TimeUnit periodUnit) {
        
        this.period = period;
        this.periodUnit = periodUnit;
        return this;
    }
    
    public RequestHandler every(long period, TimeUnit periodUnit, int times) {
        
        this.periodTimes = times;
        return every(period, periodUnit);
    }
    
    public RequestHandler withTimeout(long timeout, TimeUnit timeoutUnit) {
        
        this.timeout = timeout;
        this.timeoutUnit = timeoutUnit;
        return this;
    }
    
    public RequestHandler upon(Method method, String resource) {
        
        this.upon = new Upon(method, resource, this);
        container.addUponHandler(this.upon);
        return this;
    }
    
    public RequestHandler upon(Method method, String resource, String contentType) {
        
        this.upon = new Upon(method, resource, contentType, this);
        container.addUponHandler(this.upon);
        return this;
    }
    
    public RequestHandler withRedirect(String location) {
        
        return withRedirect(location, Status.FOUND.code);
    }
    
    public RequestHandler withRedirect(String location, int statusCode) {
        
        this.statusCode = statusCode;
        this.contentType = "text/plain";
        this.body = "";
        withHeader("Location", location);
        withHeader("Connection", "close");
        return this;
    }
    
    /*-----------------------------------------*/
    
    public int statusCode() {
        return statusCode;
    }

    public String contentType() {
        return contentType;
    }

    public ResponseBody body(HttpRequest request, Response resp, 