All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.sinistral.proteus.server.ServerResponse Maven / Gradle / Ivy


package io.sinistral.proteus.server;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.google.inject.Inject;
import io.sinistral.proteus.server.predicates.ServerPredicates;
import io.sinistral.proteus.wrappers.JsonViewWrapper;
import io.undertow.io.IoCallback;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.Cookie;
import io.undertow.server.handlers.ExceptionHandler;
import io.undertow.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.core.Response;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author jbauer
 * Base server response. Friendlier interface to underlying exchange.
 * @TODO extend javax.ws.rs.core.Response
 */

public class ServerResponse
{
    private static Logger log = LoggerFactory.getLogger(ServerResponse.class.getCanonicalName());

    @Inject
    protected static XmlMapper XML_MAPPER;

    @Inject
    protected static ObjectMapper OBJECT_MAPPER;

    protected static Map, ObjectWriter> WRITER_CACHE = new ConcurrentHashMap<>();

    protected ByteBuffer body;

    protected int status = StatusCodes.OK;
    protected final HeaderMap headers = new HeaderMap();
    protected final Map cookies = new HashMap<>();
    protected String contentType = javax.ws.rs.core.MediaType.APPLICATION_JSON;
    protected T entity;
    protected Throwable throwable;
    //	protected Class jsonContext;
    protected HttpString method = null;
    protected IoCallback ioCallback;
    protected boolean hasCookies = false;
    protected boolean hasHeaders = false;
    protected boolean hasIoCallback = false;
    protected boolean processXml = false;
    protected boolean processJson = false;
    protected boolean preprocessed = false;
    protected String location = null;

    public ServerResponse()
    {

    }

    public ByteBuffer getBody()
    {
        return body;
    }

    public int getStatus()
    {
        return this.status;
    }

    public Map getCookies()
    {
        return this.cookies;
    }

    public HeaderMap getHeaders()
    {
        return this.headers;
    }

    public ServerResponse addHeader(HttpString headerName, String headerValue)
    {
        this.headers.add(headerName, headerValue);
        this.hasHeaders = true;

        return this;
    }

    public ServerResponse addHeader(String headerString, String headerValue)
    {
        HttpString headerName = HttpString.tryFromString(headerString);

        this.headers.add(headerName, headerValue);
        this.hasHeaders = true;

        return this;
    }

    public ServerResponse setHeader(HttpString headerName, String headerValue)
    {
        this.headers.put(headerName, headerValue);
        this.hasHeaders = true;

        return this;
    }

    public ServerResponse setHeader(String headerString, String headerValue)
    {
        HttpString headerName = HttpString.tryFromString(headerString);

        this.headers.put(headerName, headerValue);
        this.hasHeaders = true;

        return this;
    }

    /**
     * @return the contentType
     */
    public String getContentType()
    {
        return contentType;
    }

    /**
     * @return the callback
     */
    public IoCallback getIoCallback()
    {
        return ioCallback;
    }

    /**
     * @param ioCallback
     *            the ioCallback to set
     */
    public void setIoCallback(IoCallback ioCallback)
    {
        this.ioCallback = ioCallback;
    }

    /**
     * @param body
     *            the body to set
     */
    public void setBody(ByteBuffer body)
    {
        this.body = body;
    }

    /**
     * @param status
     *            the status to set
     */
    public void setStatus(int status)
    {
        this.status = status;
    }

    /**
     * @param status
     *            the status to set
     */
    public void setStatus(Response.Status status)
    {
        this.status = status.getStatusCode();
    }


    public ServerResponse body(ByteBuffer body)
    {
        this.body = body;
        this.preprocessed = true;
        return this;
    }

    public ServerResponse body(byte[] body)
    {
        this.body = ByteBuffer.wrap(body);
        this.preprocessed = true;
        return this;
    }

    public ServerResponse body(String body)
    {
        return this.body(ByteBuffer.wrap(body.getBytes()));
    }

    public ServerResponse entity(T entity)
    {
        this.entity = entity;
        this.preprocessed = false;

        return this;
    }

    public ServerResponse method(HttpString method)
    {
        this.method = method;
        return this;
    }

    public ServerResponse method(String method)
    {
        this.method = Methods.fromString(method);
        return this;
    }

    public ServerResponse lastModified(Date date)
    {
        this.headers.put(Headers.LAST_MODIFIED, date.getTime());
        return this;
    }

    public ServerResponse contentLanguage(Locale locale)
    {
        this.headers.put(Headers.CONTENT_LANGUAGE, locale.toLanguageTag());
        return this;
    }

    public ServerResponse contentLanguage(String language)
    {
        this.headers.put(Headers.CONTENT_LANGUAGE, language);
        return this;
    }

    public ServerResponse throwable(Throwable throwable)
    {
        this.throwable = throwable;

        if (this.status == StatusCodes.ACCEPTED) {
            return badRequest(throwable);
        }

        return this;
    }

    public ServerResponse status(Response.Status status)
    {
        this.status = status.getStatusCode();
        return this;
    }

    public ServerResponse status(int status)
    {
        this.status = status;
        return this;
    }

    public ServerResponse header(HttpString headerName, String value)
    {
        this.headers.put(headerName, value);
        this.hasHeaders = true;
        return this;
    }

    public ServerResponse cookie(String cookieName, Cookie cookie)
    {
        this.cookies.put(cookieName, cookie);
        this.hasCookies = true;
        return this;
    }


    /**
     * @param contentType
     *            the contentType to set
     */
    protected void setContentType(String contentType)
    {
        this.contentType = contentType;

        if (this.contentType.contains(javax.ws.rs.core.MediaType.APPLICATION_JSON)) {
            if (!this.preprocessed) {
                this.processJson = true;
            }
        } else if (this.contentType.contains(javax.ws.rs.core.MediaType.APPLICATION_XML)) {
            if (!this.preprocessed) {
                this.processXml = true;
            }
        }
    }

    public ServerResponse contentType(String contentType)
    {
        this.setContentType(contentType);
        return this;
    }


    public ServerResponse contentType(javax.ws.rs.core.MediaType mediaType)
    {
        this.setContentType(mediaType.toString());
        return this;
    }

    public ServerResponse contentType(MediaType mediaType)
    {
        this.setContentType(mediaType.contentType());
        return this;
    }

    public ServerResponse applicationJson()
    {
        if (!this.preprocessed) {
            this.processJson = true;
        }
        this.contentType = javax.ws.rs.core.MediaType.APPLICATION_JSON;
        return this;
    }

    public ServerResponse textHtml()
    {
        this.contentType = javax.ws.rs.core.MediaType.TEXT_HTML;
        return this;
    }

    public ServerResponse applicationOctetStream()
    {
        this.contentType = javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM;
        return this;
    }

    public ServerResponse applicationXml()
    {
        if (!this.preprocessed) {
            this.processXml = true;
        }
        this.contentType = javax.ws.rs.core.MediaType.APPLICATION_XML;
        return this;
    }

    public ServerResponse textPlain()
    {
        this.contentType = javax.ws.rs.core.MediaType.TEXT_PLAIN;
        return this;
    }

//	public ServerResponse jsonContext(Class context)
//	{
//		this.jsonContext = context;
//		return this;
//	}

    public ServerResponse ok()
    {
        this.status = StatusCodes.OK;
        return this;
    }

    public ServerResponse redirect(String location)
    {
        this.location = location;
        this.status = StatusCodes.FOUND;
        return this;
    }

    public ServerResponse redirect(String location, int status)
    {
        this.location = location;
        this.status = status;
        return this;
    }

    public ServerResponse redirectPermanently(String location)
    {
        this.location = location;
        this.status = StatusCodes.MOVED_PERMANENTLY;
        return this;
    }

    public ServerResponse found()
    {
        this.status = StatusCodes.FOUND;
        return this;
    }


    public ServerResponse accepted()
    {
        this.status = StatusCodes.ACCEPTED;
        return this;
    }

    public ServerResponse badRequest()
    {
        this.status = StatusCodes.BAD_REQUEST;
        return this;
    }

    public ServerResponse badRequest(Throwable t)
    {
        this.throwable = t;
        return this.badRequest();
    }

    public ServerResponse badRequest(String message)
    {
        return this.errorMessage(message).badRequest();
    }

    public ServerResponse internalServerError()
    {
        this.status = StatusCodes.INTERNAL_SERVER_ERROR;
        return this;
    }

    public ServerResponse internalServerError(Throwable t)
    {
        this.throwable = t;
        return this.internalServerError();
    }

    public ServerResponse internalServerError(String message)
    {
        return this.errorMessage(message).internalServerError();
    }

    public ServerResponse created()
    {
        this.status = StatusCodes.CREATED;
        return this;
    }

    public ServerResponse created(String location)
    {
        this.status = StatusCodes.CREATED;
        this.location = location;
        return this;
    }

    public ServerResponse created(URI uri)
    {
        this.status = StatusCodes.CREATED;
        this.location = uri.toString();
        return this;
    }

    public ServerResponse notModified()
    {
        this.status = StatusCodes.NOT_MODIFIED;
        return this;
    }


    public ServerResponse notFound()
    {
        this.status = StatusCodes.NOT_FOUND;
        return this;
    }

    public ServerResponse notFound(Throwable t)
    {
        this.throwable = t;
        return this.notFound();
    }

    public ServerResponse notFound(String message)
    {
        return this.errorMessage(message).notFound();
    }


    public ServerResponse forbidden()
    {
        this.status = StatusCodes.FORBIDDEN;
        return this;
    }

    public ServerResponse forbidden(Throwable t)
    {
        this.throwable = t;
        return this.forbidden();
    }

    public ServerResponse forbidden(String message)
    {
        return this.errorMessage(message).forbidden();
    }

    public ServerResponse noContent()
    {
        this.status = StatusCodes.NO_CONTENT;
        return this;
    }

    public ServerResponse noContent(Throwable t)
    {
        this.throwable = t;
        return this.noContent();
    }


    public ServerResponse noContent(String message)
    {
        return this.errorMessage(message).noContent();
    }

    public ServerResponse serviceUnavailable()
    {
        this.status = StatusCodes.SERVICE_UNAVAILABLE;
        return this;
    }

    public ServerResponse serviceUnavailable(Throwable t)
    {
        this.throwable = t;
        return this.serviceUnavailable();
    }


    public ServerResponse serviceUnavailable(String message)
    {
        return this.errorMessage(message).serviceUnavailable();
    }

    public ServerResponse unauthorized()
    {
        this.status = StatusCodes.UNAUTHORIZED;
        return this;
    }

    public ServerResponse unauthorized(Throwable t)
    {
        this.throwable = t;
        return this.unauthorized();
    }


    public ServerResponse unauthorized(String message)
    {
        return this.errorMessage(message).unauthorized();
    }

    public ServerResponse errorMessage(String message)
    {
        this.throwable = new Throwable(message);
        return this;
    }

    public ServerResponse withIoCallback(IoCallback ioCallback)
    {
        this.ioCallback = ioCallback;
        this.hasIoCallback = ioCallback == null;
        return this;
    }


    public void send(final HttpServerExchange exchange) throws RuntimeException
    {
        send(null, exchange);
    }

    public void send(final HttpHandler handler, final HttpServerExchange exchange) throws RuntimeException
    {

        final boolean hasBody = this.body != null;
        final boolean hasEntity = this.entity != null;
        final boolean hasError = this.throwable != null;

        if(exchange.isResponseStarted())
        {
            return;
        }



        exchange.setStatusCode(this.status);


        if (hasError) {
            if(this.status == StatusCodes.OK)
            {
                exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR);
            }
            exchange.putAttachment(ExceptionHandler.THROWABLE, throwable);
            exchange.endExchange();
            return;
        }

        if (this.location != null) {
            exchange.getResponseHeaders().put(Headers.LOCATION, this.location);
        }

        if (this.status == StatusCodes.FOUND || this.status == StatusCodes.MOVED_PERMANENTLY || this.status == StatusCodes.TEMPORARY_REDIRECT || this.status == StatusCodes.SEE_OTHER || this.status == StatusCodes.PERMANENT_REDIRECT) {
            if ((this.status == StatusCodes.FOUND || this.status == StatusCodes.MOVED_PERMANENTLY) && (this.method != null)) {
                exchange.setRequestMethod(this.method);
            }

            exchange.endExchange();
            return;
        }

        if (this.contentType != null) {
            exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, this.contentType);
        }


        if (this.hasHeaders) {
            long itr = this.headers.fastIterateNonEmpty();

            while (itr != -1L) {
                final HeaderValues values = this.headers.fiCurrent(itr);

                exchange.getResponseHeaders().putAll(values.getHeaderName(), values);

                itr = this.headers.fiNextNonEmpty(itr);
            }
        }

        if (this.hasCookies) {
            exchange.getResponseCookies().putAll(this.cookies);
        } else if (!this.processJson && !this.processXml) {
            if (ServerPredicates.ACCEPT_JSON_PREDICATE.resolve(exchange)) {
                this.applicationJson();
                exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, this.contentType);
            } else if (ServerPredicates.ACCEPT_XML_PREDICATE.resolve(exchange)) {
                this.applicationXml();
                exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, this.contentType);
            } else if (ServerPredicates.ACCEPT_TEXT_PREDICATE.resolve(exchange)) {
                this.textPlain();
                exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, this.contentType);
            }
        }


        if (hasBody) {
            if (!this.hasIoCallback) {
                exchange.getResponseSender().send(this.body);
            } else {
                exchange.getResponseSender().send(this.body, this.ioCallback);
            }
        } else if (hasEntity) {
            try {
                if (this.processXml) {
                    exchange.getResponseSender().send(ByteBuffer.wrap(XML_MAPPER.writeValueAsBytes(this.entity)));
                } else {

                    final Class jsonViewClass = exchange.getAttachment(JsonViewWrapper.JSON_VIEW_KEY);

                    if(jsonViewClass != null)
                    {
                        ObjectWriter writer = WRITER_CACHE.computeIfAbsent(jsonViewClass, (view) -> OBJECT_MAPPER.writerWithView(view));
                        exchange.getResponseSender().send(ByteBuffer.wrap(writer.writeValueAsBytes(this.entity)));
                    }
                    else
                    {
                        exchange.getResponseSender().send(ByteBuffer.wrap(OBJECT_MAPPER.writeValueAsBytes(this.entity)));
                    }

                 }

            } catch (Exception e) {

                log.error(e.getMessage() + " for entity " + this.entity, e);

                throw new IllegalArgumentException(e);
            }

        } else {

            if(handler != null)
            {
                try {
                    handler.handleRequest(exchange);
                } catch (Exception e) {
                    log.error("Error handling request",e);
                    exchange.endExchange();
                }
            }
            else {
                exchange.endExchange();
            }
        }

    }

    /**
     * Creates builder to build {@link ServerResponse}.
     *
     * @return created builder
     */
    public static  ServerResponse response(Class clazz)
    {
        return new ServerResponse();
    }


    public static ServerResponse response(ByteBuffer body)
    {
        return new ServerResponse().body(body);
    }

    public static ServerResponse response(String body)
    {
        return new ServerResponse().body(body);
    }

    public static  ServerResponse response(T entity)
    {
        return new ServerResponse().entity(entity);
    }

    @SuppressWarnings("rawtypes")
    public static ServerResponse response()
    {
        return new ServerResponse();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy