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

io.mangoo.routing.handlers.RequestHandler Maven / Gradle / Ivy

The newest version!
package io.mangoo.routing.handlers;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.mangoo.annotations.FilterWith;
import io.mangoo.core.Application;
import io.mangoo.enums.Binding;
import io.mangoo.exceptions.MangooTemplateEngineException;
import io.mangoo.interfaces.filters.OncePerRequestFilter;
import io.mangoo.routing.Attachment;
import io.mangoo.routing.Response;
import io.mangoo.routing.bindings.Request;
import io.mangoo.templating.TemplateContext;
import io.mangoo.utils.JsonUtils;
import io.mangoo.utils.RequestUtils;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.Cookie;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class RequestHandler implements HttpHandler {
    private static final String FILTER_METHOD = "execute";
    private Attachment attachment;
    
    @Override
    public void handleRequest(HttpServerExchange exchange) throws Exception {
        attachment = exchange.getAttachment(RequestUtils.getAttachmentKey());
        attachment.setBody(getRequestBody(exchange));
        attachment.setRequest(getRequest(exchange));

        var response = getResponse(exchange);
        response.getCookies().forEach(exchange::setResponseCookie);

        attachment.setResponse(response);

        exchange.putAttachment(RequestUtils.getAttachmentKey(), attachment);
        nextHandler(exchange);
    }

    /**
     * Creates a new request object containing the current request data
     *
     * @param exchange The Undertow HttpServerExchange
     */
    protected Request getRequest(HttpServerExchange exchange) {
        return new Request(exchange)
                .withSession(attachment.getSession())
                .withAuthentication(attachment.getAuthentication())
                .withParameter(attachment.getRequestParameter())
                .withBody(attachment.getBody());
    }

    /**
     * Execute filters if exists in the following order:
     * RequestFilter, ControllerFilter, MethodFilter
     *
     * @param exchange The Undertow HttpServerExchange
     * @return A Response object that will be merged to the final response
     *
     * @throws NoSuchMethodException when no method is found
     * @throws IllegalAccessException when an illegal access occurs
     * @throws InvocationTargetException when an invocation fails
     * @throws MangooTemplateEngineException when the template rendering fails
     */
    protected Response getResponse(HttpServerExchange exchange) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, MangooTemplateEngineException {
        //execute global request filter
        var response = Response.ok();
        if (attachment.hasRequestFilter()) {
            final OncePerRequestFilter mangooRequestFilter = Application.getInstance(OncePerRequestFilter.class);
            response = mangooRequestFilter.execute(attachment.getRequest(), response);
        }

        if (response.isEndResponse()) {
            return response;
        }

        //execute controller filters
        response = executeFilter(attachment.getClassAnnotations(), response);
        if (response.isEndResponse()) {
            return response;
        }

        //execute method filters
        response = executeFilter(attachment.getMethodAnnotations(), response);
        if (response.isEndResponse()) {
            return response;
        }

        if (response.isRedirect()) {
            return response;
        }

        return invokeController(exchange, response);
    }

    /**
     * Invokes the controller methods and retrieves the response which
     * is later send to the client
     *
     * @param exchange The Undertow HttpServerExchange
     * @return A response object
     *
     * @throws IllegalAccessException when an illegal access occurs
     * @throws InvocationTargetException when an invocation fails
     * @throws MangooTemplateEngineException when the template rendering fails
     */
    protected Response invokeController(HttpServerExchange exchange, Response response) throws IllegalAccessException, InvocationTargetException, MangooTemplateEngineException {
        Response invokedResponse;

        if (attachment.getMethodParameters().isEmpty()) {
            invokedResponse = (Response) attachment.getMethod().invoke(attachment.getControllerInstance());
        } else {
            final Object [] convertedParameters = getConvertedParameters(exchange);
            invokedResponse = (Response) attachment.getMethod().invoke(attachment.getControllerInstance(), convertedParameters);
        }

        invokedResponse.render(response.getContent());
        invokedResponse.headers(response.getHeaders());
        
        if (invokedResponse.isRendered()) {
            var templateContext = new TemplateContext(invokedResponse.getContent())
                    .withFlash(attachment.getFlash())
                    .withSession(attachment.getSession())
                    .withForm(attachment.getForm())
                    .withMessages(attachment.getMessages())
                    .withController(attachment.getControllerAndMethod())
                    .withPrettyTime(attachment.getLocale())
                    .withTemplatePath(getTemplatePath(invokedResponse));
            
            invokedResponse.bodyHtml(attachment.getTemplateEngine().renderTemplate(templateContext));
        }

        for (Cookie cookie : response.getCookies()) {
            invokedResponse.cookie(cookie);
        }

        return invokedResponse;
    }

    /**
     * Returns the complete path to the template based on the
     * controller and method name
     *
     * @param response The current response
     *
     * @return A case-sensitive template path, e.g. /ApplicationController/index.ftl
     */
    protected String getTemplatePath(Response response) {
        return StringUtils.isBlank(response.getTemplate()) ? (attachment.getControllerClassName() + "/" + attachment.getTemplateEngine().getTemplateName(attachment.getControllerMethodName())) : response.getTemplate();
    }

    /**
     * Creates an array with the request controller method parameter and sets the appropriate values
     *
     * @param exchange The Undertow HttpServerExchange
     * @return an array with the request controller method parameter and sets the appropriate values
     */
    @SuppressFBWarnings(justification = "Intentionally adding unrelated types", value = "UCC_UNRELATED_COLLECTION_CONTENTS")
    protected Object[] getConvertedParameters(HttpServerExchange exchange) {
        final var convertedParameters = new Object[attachment.getMethodParametersCount()];

        var index = 0;
        for (final Map.Entry> entry : attachment.getMethodParameters().entrySet()) {
            final String key = entry.getKey();
            final Class clazz = entry.getValue();
            final var binding = Optional.ofNullable(Binding.fromString(clazz.getName())).orElse(Binding.UNDEFINED);

            convertedParameters[index] = switch (binding) {
                case FORM -> attachment.getForm();
                case AUTHENTICATION -> attachment.getAuthentication();
                case SESSION -> attachment.getSession();
                case FLASH -> attachment.getFlash();
                case REQUEST -> attachment.getRequest();
                case MESSAGES -> attachment.getMessages();
                case LOCAL_DATE -> StringUtils.isBlank(attachment.getRequestParameter().get(key)) ? null : LocalDate.parse(attachment.getRequestParameter().get(key));
                case LOCAL_DATE_TIME -> StringUtils.isBlank(attachment.getRequestParameter().get(key)) ? null : LocalDateTime.parse(attachment.getRequestParameter().get(key));
                case STRING -> StringUtils.isBlank(attachment.getRequestParameter().get(key)) ? null : attachment.getRequestParameter().get(key);
                case INT_PRIMITIVE -> StringUtils.isBlank(attachment.getRequestParameter().get(key)) ? 0 : Integer.parseInt(attachment.getRequestParameter().get(key));
                case INTEGER -> StringUtils.isBlank(attachment.getRequestParameter().get(key)) ? null : Integer.valueOf(attachment.getRequestParameter().get(key));
                case DOUBLE_PRIMITIVE -> StringUtils.isBlank(attachment.getRequestParameter().get(key)) ? 0 : Double.parseDouble(attachment.getRequestParameter().get(key));
                case DOUBLE -> StringUtils.isBlank(attachment.getRequestParameter().get(key)) ? null : Double.valueOf(attachment.getRequestParameter().get(key));
                case FLOAT_PRIMITIVE -> StringUtils.isBlank(attachment.getRequestParameter().get(key)) ? 0 : Float.parseFloat(attachment.getRequestParameter().get(key));
                case FLOAT -> StringUtils.isBlank(attachment.getRequestParameter().get(key)) ? null : Float.valueOf(attachment.getRequestParameter().get(key));
                case LONG_PRIMITIVE -> StringUtils.isBlank(attachment.getRequestParameter().get(key)) ? 0 : Long.parseLong(attachment.getRequestParameter().get(key));
                case LONG -> StringUtils.isBlank(attachment.getRequestParameter().get(key)) ? null : Long.valueOf(attachment.getRequestParameter().get(key));
                case OPTIONAL -> StringUtils.isBlank(attachment.getRequestParameter().get(key)) ? Optional.empty() : Optional.of(attachment.getRequestParameter().get(key));
                case UNDEFINED -> RequestUtils.isJsonRequest(exchange) ? JsonUtils.toObject(attachment.getBody(), clazz) : null;
                default -> null;
            };

            index++;
        }

        return convertedParameters;
    }

    /**
     * Executes all filters on controller and method level
     *
     * @param annotations An array of @FilterWith annotated classes and methods
     * @param response The response to use
     * @return The updated response
     *
     * @throws NoSuchMethodException when the method is not found
     * @throws IllegalAccessException when an illegal access occurs
     * @throws InvocationTargetException when the target is not found
     */
    protected Response executeFilter(List annotations, Response response) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        for (final Annotation annotation : annotations) {
            final var filterWith = (FilterWith) annotation;
            for (final Class clazz : filterWith.value()) {
                if (response.isEndResponse()) {
                    return response;
                } else {
                    final var classMethod = clazz.getMethod(FILTER_METHOD, Request.class, Response.class);
                    response = (Response) classMethod.invoke(Application.getInstance(clazz), attachment.getRequest(), response);
                }
            }
        }

        return response;
    }

    /**
     * Retrieves the complete request body from the request
     *
     * @param exchange The Undertow HttpServerExchange
     * @return A body object containing the request body
     *
     * @throws IOException when setting the body fails
     */
    protected String getRequestBody(HttpServerExchange exchange) throws IOException {
        var body = Strings.EMPTY;
        if (RequestUtils.isPostPutPatch(exchange)) {
            exchange.startBlocking();
            body = IOUtils.toString(exchange.getInputStream(), StandardCharsets.UTF_8);
        }

        return body;
    }

    /**
     * Handles the next request in the handler chain
     *
     * @param exchange The HttpServerExchange
     * @throws Exception Thrown when an exception occurs
     */
    protected void nextHandler(HttpServerExchange exchange) throws Exception {
        Application.getInstance(OutboundCookiesHandler.class).handleRequest(exchange);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy