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

io.micronaut.web.router.AnnotatedMethodRouteBuilder Maven / Gradle / Ivy

There is a newer version: 4.6.5
Show newest version
/*
 * Copyright 2017-2019 original authors
 *
 * 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 io.micronaut.web.router;

import io.micronaut.context.ExecutionHandleLocator;
import io.micronaut.context.processor.ExecutableMethodProcessor;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.reflect.ClassLoadingReporter;
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Error;
import io.micronaut.http.annotation.*;
import io.micronaut.http.uri.UriTemplate;
import io.micronaut.inject.BeanDefinition;
import io.micronaut.inject.ExecutableMethod;

import javax.inject.Singleton;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;

/**
 * Responsible for building {@link Route} instances for the annotations found in the {@code io.micronaut.http.annotation}
 * package.
 *
 * @author Graeme Rocher
 * @since 1.0
 */
@Singleton
public class AnnotatedMethodRouteBuilder extends DefaultRouteBuilder implements ExecutableMethodProcessor {

    private static final MediaType[] DEFAULT_MEDIA_TYPES = {MediaType.APPLICATION_JSON_TYPE};
    private final Map> httpMethodsHandlers = new LinkedHashMap<>();

    /**
     * @param executionHandleLocator The execution handler locator
     * @param uriNamingStrategy The URI naming strategy
     * @param conversionService The conversion service
     */
    public AnnotatedMethodRouteBuilder(ExecutionHandleLocator executionHandleLocator, UriNamingStrategy uriNamingStrategy, ConversionService conversionService) {
        super(executionHandleLocator, uriNamingStrategy, conversionService);
        httpMethodsHandlers.put(Get.class, (BeanDefinition bean, ExecutableMethod method) -> {
            String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI);
            MediaType[] produces = resolveProduces(method);
            Route route = GET(resolveUri(bean, uri,
                method,
                uriNamingStrategy),
                bean,
                method).produces(produces);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Created Route: {}", route);
            }
            route = HEAD(resolveUri(bean, uri,
                    method,
                    uriNamingStrategy),
                    bean,
                    method).produces(produces);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Created Route: {}", route);
            }
        });

        httpMethodsHandlers.put(Post.class, (BeanDefinition bean, ExecutableMethod method) -> {
            String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI);
            MediaType[] consumes = resolveConsumes(method);
            MediaType[] produces = resolveProduces(method);
            Route route = POST(resolveUri(bean, uri,
                method,
                uriNamingStrategy),
                bean,
                method);
            route = route.consumes(consumes).produces(produces);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Created Route: {}", route);
            }
        });

        httpMethodsHandlers.put(Put.class, (BeanDefinition bean, ExecutableMethod method) -> {
            String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI);
            MediaType[] consumes = resolveConsumes(method);
            MediaType[] produces = resolveProduces(method);
            Route route = PUT(resolveUri(bean, uri,
                method,
                uriNamingStrategy),
                bean,
                method);
            route = route.consumes(consumes).produces(produces);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Created Route: {}", route);
            }
        });

        httpMethodsHandlers.put(Patch.class, (BeanDefinition bean, ExecutableMethod method) -> {
            String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI);
            MediaType[] consumes = resolveConsumes(method);
            MediaType[] produces = resolveProduces(method);
            Route route = PATCH(resolveUri(bean, uri,
                method,
                uriNamingStrategy),
                bean,
                method);
            route = route.consumes(consumes).produces(produces);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Created Route: {}", route);
            }
        });

        httpMethodsHandlers.put(Delete.class, (BeanDefinition bean, ExecutableMethod method) -> {
            String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI);
            MediaType[] consumes = resolveConsumes(method);
            MediaType[] produces = resolveProduces(method);
            Route route = DELETE(resolveUri(bean, uri,
                method,
                uriNamingStrategy),
                bean,
                method);
            route = route.consumes(consumes).produces(produces);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Created Route: {}", route);
            }
        });


        httpMethodsHandlers.put(Head.class, (BeanDefinition bean, ExecutableMethod method) -> {
            String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI);
            Route route = HEAD(resolveUri(bean, uri,
                method,
                uriNamingStrategy),
                bean,
                method);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Created Route: {}", route);
            }
        });

        httpMethodsHandlers.put(Options.class, (BeanDefinition bean, ExecutableMethod method) -> {
            String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI);
            MediaType[] consumes = resolveConsumes(method);
            MediaType[] produces = resolveProduces(method);
            Route route = OPTIONS(resolveUri(bean, uri,
                method,
                uriNamingStrategy),
                bean,
                method);
            route = route.consumes(consumes).produces(produces);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Created Route: {}", route);
            }
        });

        httpMethodsHandlers.put(Trace.class, (BeanDefinition bean, ExecutableMethod method) -> {
            String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI);
            Route route = TRACE(resolveUri(bean, uri,
                method,
                uriNamingStrategy),
                bean,
                method);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Created Route: {}", route);
            }
        });

        httpMethodsHandlers.put(Error.class, (BeanDefinition bean, ExecutableMethod method) -> {
                boolean isGlobal = method.isTrue(Error.class, "global");
                Class declaringType = bean.getBeanType();
                if (method.isPresent(Error.class, "status")) {
                    Optional value = method.enumValue(Error.class, "status", HttpStatus.class);
                    value.ifPresent(httpStatus -> {
                        if (isGlobal) {
                            status(httpStatus, declaringType, method.getMethodName(), method.getArgumentTypes());
                        } else {
                            status(declaringType, httpStatus, declaringType, method.getMethodName(), method.getArgumentTypes());
                        }
                    });
                } else {
                    Class exceptionType = null;
                    if (method.isPresent(Error.class, AnnotationMetadata.VALUE_MEMBER)) {
                        Optional annotationValue = method.classValue(Error.class);
                        if (annotationValue.isPresent()) {
                            if (Throwable.class.isAssignableFrom(annotationValue.get())) {
                                exceptionType = annotationValue.get();
                            }
                        }
                    }
                    if (exceptionType == null) {
                        exceptionType = Arrays.stream(method.getArgumentTypes())
                                .filter(Throwable.class::isAssignableFrom)
                                .findFirst()
                                .orElse(Throwable.class);
                    }

                    if (isGlobal) {
                        //noinspection unchecked
                        error(exceptionType, declaringType, method.getMethodName(), method.getArgumentTypes());
                    } else {
                        //noinspection unchecked
                        error(declaringType, exceptionType, declaringType, method.getMethodName(), method.getArgumentTypes());
                    }
                }
            }
        );
    }

    private MediaType[] resolveConsumes(ExecutableMethod method) {
        MediaType[] consumes = MediaType.of(method.stringValues(Consumes.class));
        if (ArrayUtils.isEmpty(consumes)) {
            consumes = DEFAULT_MEDIA_TYPES;
        }
        return consumes;
    }

    private MediaType[] resolveProduces(ExecutableMethod method) {
        MediaType[] produces = MediaType.of(method.stringValues(Produces.class));
        if (ArrayUtils.isEmpty(produces)) {
            produces = DEFAULT_MEDIA_TYPES;
        }
        return produces;
    }

    @Override
    public void process(BeanDefinition beanDefinition, ExecutableMethod method) {
        Optional> actionAnn = method.getAnnotationTypeByStereotype(HttpMethodMapping.class);
        actionAnn.ifPresent(annotationClass -> {
                BiConsumer handler = httpMethodsHandlers.get(annotationClass);
                if (handler != null) {
                    ClassLoadingReporter.reportBeanPresent(method.getReturnType().getType());
                    for (Class argumentType : method.getArgumentTypes()) {
                        ClassLoadingReporter.reportBeanPresent(argumentType);
                    }
                    handler.accept(beanDefinition, method);
                }
            }
        );

        if (!actionAnn.isPresent() && method.isDeclaredAnnotationPresent(UriMapping.class)) {
            String uri = method.stringValue(UriMapping.class).orElse(UriMapping.DEFAULT_URI);
            MediaType[] produces = MediaType.of(method.stringValues(Produces.class));
            Route route = GET(resolveUri(beanDefinition, uri,
                    method,
                    uriNamingStrategy),
                    method.getDeclaringType(),
                    method.getMethodName(),
                    method.getArgumentTypes()).produces(produces);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Created Route: {}", route);
            }
        }

    }

    private String resolveUri(BeanDefinition bean, String value, ExecutableMethod method, UriNamingStrategy uriNamingStrategy) {
        UriTemplate rootUri = UriTemplate.of(uriNamingStrategy.resolveUri(bean));
        if (StringUtils.isNotEmpty(value)) {
            return rootUri.nest(value).toString();
        } else {
            return rootUri.nest(uriNamingStrategy.resolveUri(method.getMethodName())).toString();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy