Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.micronaut.web.router.AnnotatedMethodRouteBuilder Maven / Gradle / Ivy
/*
* Copyright 2017-2020 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
*
* https://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.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.http.HttpMethod;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Consumes;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.CustomHttpMethod;
import io.micronaut.http.annotation.Delete;
import io.micronaut.http.annotation.Error;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Head;
import io.micronaut.http.annotation.HttpMethodMapping;
import io.micronaut.http.annotation.Options;
import io.micronaut.http.annotation.Patch;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.annotation.Produces;
import io.micronaut.http.annotation.Put;
import io.micronaut.http.annotation.Trace;
import io.micronaut.http.annotation.UriMapping;
import io.micronaut.http.uri.UriTemplate;
import io.micronaut.inject.BeanDefinition;
import io.micronaut.inject.ExecutableMethod;
import jakarta.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.Set;
import java.util.function.Consumer;
/**
* 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, Consumer> 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, (RouteDefinition definition) -> {
final BeanDefinition bean = definition.beanDefinition;
final ExecutableMethod method = definition.executableMethod;
Set uris = this.resolveUrisMapping(Get.class, method);
for (String uri: uris) {
MediaType[] produces = resolveProduces(method);
UriRoute route = GET(resolveUri(bean, uri,
method,
uriNamingStrategy),
bean,
method).produces(produces);
if (definition.port > -1) {
route.exposedPort(definition.port);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Created Route: {}", route);
}
if (method.booleanValue(Get.class, "headRoute").orElse(true)) {
route = HEAD(resolveUri(bean, uri,
method,
uriNamingStrategy),
bean,
method).produces(produces);
if (definition.port > -1) {
route.exposedPort(definition.port);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Created Route: {}", route);
}
}
}
});
httpMethodsHandlers.put(Post.class, (RouteDefinition definition) -> {
final ExecutableMethod method = definition.executableMethod;
final BeanDefinition bean = definition.beanDefinition;
Set uris = this.resolveUrisMapping(Post.class, method);
for (String uri: uris) {
MediaType[] consumes = resolveConsumes(method);
MediaType[] produces = resolveProduces(method);
UriRoute route = POST(resolveUri(bean, uri,
method,
uriNamingStrategy),
bean,
method);
route = route.consumes(consumes).produces(produces);
if (definition.port > -1) {
route.exposedPort(definition.port);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Created Route: {}", route);
}
}
});
httpMethodsHandlers.put(CustomHttpMethod.class, (RouteDefinition definition) -> {
final ExecutableMethod method = definition.executableMethod;
final BeanDefinition bean = definition.beanDefinition;
Set uris = this.resolveUrisMapping(CustomHttpMethod.class, method);
for (String uri: uris) {
MediaType[] consumes = resolveConsumes(method);
MediaType[] produces = resolveProduces(method);
String methodName = method.stringValue(CustomHttpMethod.class, "method").get();
UriRoute route = buildBeanRoute(methodName, HttpMethod.CUSTOM, resolveUri(bean, uri,
method,
uriNamingStrategy),
bean,
method);
route = route.consumes(consumes).produces(produces);
if (definition.port > -1) {
route.exposedPort(definition.port);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Created Route: {}", route);
}
}
});
httpMethodsHandlers.put(Put.class, (RouteDefinition definition) -> {
final ExecutableMethod method = definition.executableMethod;
final BeanDefinition bean = definition.beanDefinition;
Set uris = this.resolveUrisMapping(Put.class, method);
for (String uri: uris) {
MediaType[] consumes = resolveConsumes(method);
MediaType[] produces = resolveProduces(method);
UriRoute route = PUT(resolveUri(bean, uri,
method,
uriNamingStrategy),
bean,
method);
route = route.consumes(consumes).produces(produces);
if (definition.port > -1) {
route.exposedPort(definition.port);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Created Route: {}", route);
}
}
});
httpMethodsHandlers.put(Patch.class, (RouteDefinition definition) -> {
final ExecutableMethod method = definition.executableMethod;
final BeanDefinition bean = definition.beanDefinition;
Set uris = this.resolveUrisMapping(Patch.class, method);
for (String uri: uris) {
MediaType[] consumes = resolveConsumes(method);
MediaType[] produces = resolveProduces(method);
UriRoute route = PATCH(resolveUri(bean, uri,
method,
uriNamingStrategy),
bean,
method);
route = route.consumes(consumes).produces(produces);
if (definition.port > -1) {
route.exposedPort(definition.port);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Created Route: {}", route);
}
}
});
httpMethodsHandlers.put(Delete.class, (RouteDefinition definition) -> {
final ExecutableMethod method = definition.executableMethod;
final BeanDefinition bean = definition.beanDefinition;
Set uris = this.resolveUrisMapping(Delete.class, method);
for (String uri: uris) {
MediaType[] consumes = resolveConsumes(method);
MediaType[] produces = resolveProduces(method);
UriRoute route = DELETE(resolveUri(bean, uri,
method,
uriNamingStrategy),
bean,
method);
route = route.consumes(consumes).produces(produces);
if (definition.port > -1) {
route.exposedPort(definition.port);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Created Route: {}", route);
}
}
});
httpMethodsHandlers.put(Head.class, (RouteDefinition definition) -> {
final ExecutableMethod method = definition.executableMethod;
final BeanDefinition bean = definition.beanDefinition;
Set uris = this.resolveUrisMapping(Head.class, method);
for (String uri: uris) {
UriRoute route = HEAD(resolveUri(bean, uri,
method,
uriNamingStrategy),
bean,
method);
if (definition.port > -1) {
route.exposedPort(definition.port);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Created Route: {}", route);
}
}
});
httpMethodsHandlers.put(Options.class, (RouteDefinition definition) -> {
final ExecutableMethod method = definition.executableMethod;
final BeanDefinition bean = definition.beanDefinition;
Set uris = this.resolveUrisMapping(Options.class, method);
for (String uri: uris) {
MediaType[] consumes = resolveConsumes(method);
MediaType[] produces = resolveProduces(method);
UriRoute route = OPTIONS(resolveUri(bean, uri,
method,
uriNamingStrategy),
bean,
method);
route = route.consumes(consumes).produces(produces);
if (definition.port > -1) {
route.exposedPort(definition.port);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Created Route: {}", route);
}
}
});
httpMethodsHandlers.put(Trace.class, (RouteDefinition definition) -> {
final ExecutableMethod method = definition.executableMethod;
final BeanDefinition bean = definition.beanDefinition;
Set uris = this.resolveUrisMapping(Trace.class, method);
for (String uri: uris) {
UriRoute route = TRACE(resolveUri(bean, uri,
method,
uriNamingStrategy),
bean,
method);
if (definition.port > -1) {
route.exposedPort(definition.port);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Created Route: {}", route);
}
}
});
httpMethodsHandlers.put(Error.class, (RouteDefinition definition) -> {
final ExecutableMethod method = definition.executableMethod;
final BeanDefinition bean = definition.beanDefinition;
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 extends Throwable> exceptionType = null;
if (method.isPresent(Error.class, AnnotationMetadata.VALUE_MEMBER)) {
Optional annotationValue = method.classValue(Error.class);
if (annotationValue.isPresent() && Throwable.class.isAssignableFrom(annotationValue.get())) {
exceptionType = (Class extends Throwable>) annotationValue.get();
}
}
if (exceptionType == null) {
exceptionType = Arrays.stream(method.getArgumentTypes())
.filter(Throwable.class::isAssignableFrom)
.findFirst()
.orElse(Throwable.class);
}
if (isGlobal) {
error(exceptionType, declaringType, method.getMethodName(), method.getArgumentTypes());
} else {
error(declaringType, exceptionType, declaringType, method.getMethodName(), method.getArgumentTypes());
}
}
}
);
}
private Set resolveUrisMapping(Class extends Annotation> httpMethod, ExecutableMethod method) {
Set uris = CollectionUtils.setOf(method.stringValues(httpMethod, "uris"));
Optional uri = method.stringValue(HttpMethodMapping.class);
if (uris.isEmpty()) {
uris.add(uri.orElse(UriMapping.DEFAULT_URI));
} else {
uri.ifPresent(uris::add);
}
return uris;
}
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 -> {
Consumer handler = httpMethodsHandlers.get(annotationClass);
if (handler != null) {
final int port = beanDefinition.intValue(Controller.class, "port").orElse(-1);
handler.accept(new RouteDefinition(beanDefinition, method, port));
}
}
);
if (actionAnn.isEmpty() && method.isDeclaredAnnotationPresent(UriMapping.class)) {
Set uris = CollectionUtils.setOf(method.stringValues(UriMapping.class, "uris"));
uris.add(method.stringValue(UriMapping.class).orElse(UriMapping.DEFAULT_URI));
for (String uri: uris) {
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();
}
}
/**
* state class for defining routes.
*/
private static final class RouteDefinition {
private final BeanDefinition beanDefinition;
private final ExecutableMethod executableMethod;
private final int port;
public RouteDefinition(BeanDefinition beanDefinition, ExecutableMethod executableMethod, int port) {
this.beanDefinition = beanDefinition;
this.executableMethod = executableMethod;
this.port = port;
}
}
}