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

io.inverno.mod.web.compiler.internal.WebServerControllerConfigurerCompilerPlugin Maven / Gradle / Ivy

Go to download

Inverno web compiler module providing an Inverno compiler plugin to aggregate module's web routes and web router configurers into a single web router configurer

There is a newer version: 1.11.0
Show newest version
/*
 * Copyright 2021 Jeremy KUHN
 *
 * 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.inverno.mod.web.compiler.internal;

import io.inverno.core.annotation.Bean;
import io.inverno.core.compiler.spi.ModuleBeanInfo;
import io.inverno.core.compiler.spi.ReporterInfo;
import io.inverno.core.compiler.spi.plugin.CompilerPlugin;
import io.inverno.core.compiler.spi.plugin.PluginContext;
import io.inverno.core.compiler.spi.plugin.PluginExecution;
import io.inverno.core.compiler.spi.plugin.PluginExecutionException;
import io.inverno.mod.http.base.ExchangeContext;
import io.inverno.mod.web.server.ErrorWebInterceptorsConfigurer;
import io.inverno.mod.web.server.ErrorWebRouterConfigurer;
import io.inverno.mod.web.server.ErrorWebRoutesConfigurer;
import io.inverno.mod.web.server.WebInterceptorsConfigurer;
import io.inverno.mod.web.server.WebRouterConfigurer;
import io.inverno.mod.web.server.WebRoutesConfigurer;
import io.inverno.mod.web.server.annotation.WebController;
import io.inverno.mod.web.server.annotation.WebRoute;
import io.inverno.mod.web.server.annotation.WebRoutes;
import io.inverno.mod.web.compiler.internal.WebServerControllerConfigurerClassGenerationContext.GenerationMode;
import io.inverno.mod.web.compiler.spi.ErrorWebInterceptorsConfigurerInfo;
import io.inverno.mod.web.compiler.spi.ErrorWebRouterConfigurerInfo;
import io.inverno.mod.web.compiler.spi.ErrorWebRoutesConfigurerInfo;
import io.inverno.mod.web.compiler.spi.WebConfigurerQualifiedName;
import io.inverno.mod.web.compiler.spi.WebControllerInfo;
import io.inverno.mod.web.compiler.spi.WebExchangeContextParameterInfo;
import io.inverno.mod.web.compiler.spi.WebExchangeParameterInfo;
import io.inverno.mod.web.compiler.spi.WebInterceptorsConfigurerInfo;
import io.inverno.mod.web.compiler.spi.WebRouteInfo;
import io.inverno.mod.web.compiler.spi.WebRouterConfigurerInfo;
import io.inverno.mod.web.compiler.spi.WebRoutesConfigurerInfo;
import io.inverno.mod.web.compiler.spi.WebServerControllerConfigurerQualifiedName;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.WildcardType;

/**
 * 

* The web Inverno compiler plugin generates a {@link WebRouterConfigurer} implementation that aggregates the routes defined in the {@link WebController @WebController} beans defined in the module as * well as the {@link WebRoutes web router configurer} beans provided in the module. *

* *

* This plugin can also generates an Open API specification for all the web controllers defined in the module. This can be activated with option * {@code inverno.web.generateOpenApiDefinition}. *

* * @author Jeremy Kuhn * @since 1.0 */ public class WebServerControllerConfigurerCompilerPlugin implements CompilerPlugin { private static final String OPTION_GENERATE_OPENAPI_DEFINITION = "inverno.web.generateOpenApiDefinition"; private final WebServerControllerConfigurerOpenApiGenerator openApiGenerator; private final WebServerControllerConfigurerClassGenerator webRouterConfigurerClassGenerator; private final WebRouteClashDetector webRouteClashDetector; private PluginContext pluginContext; private TypeHierarchyExtractor typeHierarchyExtractor; private TypeMirror webControllerAnnotationType; private TypeMirror webRoutesAnnotationType; private TypeMirror webInterceptorsConfigurerType; private TypeMirror webRoutesConfigurerType; private TypeMirror webRouterConfigurerType; private TypeMirror errorWebInterceptorsConfigurerType; private TypeMirror errorWebRoutesConfigurerType; private TypeMirror errorWebRouterConfigurerType; private TypeMirror exchangeContextType; private TypeMirror objectType; private boolean enabled = true; public WebServerControllerConfigurerCompilerPlugin() { this.webRouterConfigurerClassGenerator = new WebServerControllerConfigurerClassGenerator(); this.openApiGenerator = new WebServerControllerConfigurerOpenApiGenerator(); this.webRouteClashDetector = new WebRouteClashDetector(); } @Override public Set getSupportedAnnotationTypes() { return Set.of(WebRoute.class.getCanonicalName(), WebController.class.getCanonicalName()); } @Override public Set getSupportedOptions() { return Set.of(WebServerControllerConfigurerCompilerPlugin.OPTION_GENERATE_OPENAPI_DEFINITION); } @Override public void init(PluginContext pluginContext) { this.pluginContext = pluginContext; TypeElement webControllerElement = this.pluginContext.getElementUtils().getTypeElement(WebController.class.getCanonicalName()); if(webControllerElement == null) { this.enabled = false; if(pluginContext.getOptions().isDebug()) { System.err.println("Plugin " + WebServerControllerConfigurerCompilerPlugin.class.getCanonicalName() + " disabled due to missing dependencies"); } return; } this.webControllerAnnotationType = webControllerElement.asType(); this.webRoutesAnnotationType = this.pluginContext.getElementUtils().getTypeElement(WebRoutes.class.getCanonicalName()).asType(); this.webInterceptorsConfigurerType = this.pluginContext.getTypeUtils().erasure(this.pluginContext.getElementUtils().getTypeElement(WebInterceptorsConfigurer.class.getCanonicalName()).asType()); this.webRoutesConfigurerType = this.pluginContext.getTypeUtils().erasure(this.pluginContext.getElementUtils().getTypeElement(WebRoutesConfigurer.class.getCanonicalName()).asType()); this.webRouterConfigurerType = this.pluginContext.getTypeUtils().erasure(this.pluginContext.getElementUtils().getTypeElement(WebRouterConfigurer.class.getCanonicalName()).asType()); this.errorWebInterceptorsConfigurerType = this.pluginContext.getTypeUtils().erasure(this.pluginContext.getElementUtils().getTypeElement(ErrorWebInterceptorsConfigurer.class.getCanonicalName()).asType()); this.errorWebRoutesConfigurerType = this.pluginContext.getTypeUtils().erasure(this.pluginContext.getElementUtils().getTypeElement(ErrorWebRoutesConfigurer.class.getCanonicalName()).asType()); this.errorWebRouterConfigurerType = this.pluginContext.getTypeUtils().erasure(this.pluginContext.getElementUtils().getTypeElement(ErrorWebRouterConfigurer.class.getCanonicalName()).asType()); this.exchangeContextType = this.pluginContext.getElementUtils().getTypeElement(ExchangeContext.class.getCanonicalName()).asType(); this.objectType = this.pluginContext.getElementUtils().getTypeElement(Object.class.getCanonicalName()).asType(); this.typeHierarchyExtractor = new TypeHierarchyExtractor(this.pluginContext.getTypeUtils()); } @Override public boolean canExecute(ModuleElement moduleElement) { return this.enabled && this.pluginContext.getElementUtils().getTypeElement(moduleElement, WebController.class.getCanonicalName()) != null; } private TypeMirror unwildContextType(ReporterInfo reporter, DeclaredType contextType) throws IllegalArgumentException { DeclaredType contextDeclaredType = (DeclaredType)contextType; if(!contextDeclaredType.getTypeArguments().isEmpty()) { TypeElement typeElement = (TypeElement)this.pluginContext.getTypeUtils().asElement(contextDeclaredType); List typeArguments = contextDeclaredType.getTypeArguments(); TypeMirror[] unwildTypeArgs = new TypeMirror[typeArguments.size()]; for(int i=0;i bounds = typeElement.getTypeParameters().get(i).getBounds(); switch(bounds.size()) { case 0: { //Object this.pluginContext.getElementUtils().getTypeElement(Object.class.getCanonicalName()); break; } case 1: { unwildTypeArgs[i] = bounds.get(0); break; } default: { // TODO if there are more than one bounds we have a union type: T extends Number & Runnable and we must fail because the context can't be parameterized reporter.error("Context type with union type bound is not allowed"); } } } } else { unwildTypeArgs[i] = typeArg; } } return this.pluginContext.getTypeUtils().getDeclaredType(typeElement, unwildTypeArgs); } return contextType; } @Override public void execute(PluginExecution execution) throws PluginExecutionException { WebRouteInfoFactory webRouteFactory = new WebRouteInfoFactory(this.pluginContext, execution); WebServerControllerConfigurerQualifiedName webRouterConfigurerQName = new WebServerControllerConfigurerQualifiedName(execution.getModuleQualifiedName()); this.processWebRoutes(execution, webRouteFactory); List webControllers = this.processWebControllers(execution, webRouteFactory); List webInterceptorsConfigurers = this.processWebInterceptorsConfigurers(execution); List webRoutesConfigurers = this.processWebRoutesConfigurers(execution, webRouteFactory); List webRouterConfigurers = this.processWebRouterConfigurers(execution, webRouteFactory); List errorWebInterceptorsConfigurers = this.processErrorWebInterceptorsConfigurers(execution); List errorWebRoutesConfigurers = this.processErrorWebRoutesConfigurers(execution); List errorWebRouterConfigurers = this.processErrorWebRouterConfigurers(execution); Comparator contexTypeComparator = (t1,t2) -> { if(this.pluginContext.getTypeUtils().isSameType(t1,t2)) { return 0; } return t1.toString().compareTo(t2.toString()); }; Set contextTypes = Stream.of( webControllers.stream() .flatMap(controller -> Arrays.stream(controller.getRoutes())) .flatMap(route -> Arrays.stream(route.getParameters())), webInterceptorsConfigurers.stream(), webRoutesConfigurers.stream(), webRouterConfigurers.stream(), errorWebInterceptorsConfigurers.stream(), errorWebRoutesConfigurers.stream(), errorWebRouterConfigurers.stream() ) .flatMap(Function.identity()) .flatMap(info -> { TypeMirror contextType; if(info instanceof WebExchangeParameterInfo) { contextType = ((WebExchangeParameterInfo)info).getContextType(); } else if(info instanceof WebExchangeContextParameterInfo) { contextType = ((WebExchangeContextParameterInfo)info).getType(); } else if(info instanceof WebInterceptorsConfigurerInfo) { contextType = ((WebInterceptorsConfigurerInfo)info).getContextType(); } else if(info instanceof WebRoutesConfigurerInfo) { contextType = ((WebRoutesConfigurerInfo)info).getContextType(); } else if(info instanceof WebRouterConfigurerInfo) { contextType = ((WebRouterConfigurerInfo)info).getContextType(); } else if(info instanceof ErrorWebInterceptorsConfigurerInfo) { contextType = ((ErrorWebInterceptorsConfigurerInfo)info).getContextType(); } else if(info instanceof ErrorWebRoutesConfigurerInfo) { contextType = ((ErrorWebRoutesConfigurerInfo)info).getContextType(); } else if(info instanceof ErrorWebRouterConfigurerInfo) { contextType = ((ErrorWebRouterConfigurerInfo)info).getContextType(); } else { return Stream.of(); } List actualTypes; if(contextType.getKind() == TypeKind.INTERSECTION) { actualTypes = ((IntersectionType)contextType).getBounds(); } else { actualTypes = List.of(contextType); } return Stream.concat( actualTypes.stream(), actualTypes.stream() // we only keep public super types, hidden super types should be included // this means we could have compile errors if the user specified a non-public context type explicitly .flatMap(type -> this.getAllSuperTypes(contextType).stream().filter(superContextType -> ((DeclaredType)superContextType).asElement().getModifiers().contains(Modifier.PUBLIC))) ) .map(type -> new Object[] {type, info}); }) .collect(Collectors.groupingBy(typeAndInfo -> this.pluginContext.getTypeUtils().erasure((TypeMirror)typeAndInfo[0]))) // We must retain one type per map entry .entrySet().stream().map(typesEntry -> { // We must retain the type that can be assigned to all others, if it doesn't exist we shall report an error: we have defined incompatible context types // n^2 complexity... Can we do better? Set infos = new HashSet<>(); main: for(Object[] typeAndInfo1 : typesEntry.getValue()) { TypeMirror t1 = (TypeMirror)typeAndInfo1[0]; ReporterInfo info = (ReporterInfo)typeAndInfo1[1]; infos.add(info); for(Object[] typeAndInfo2 : typesEntry.getValue()) { TypeMirror t2 = (TypeMirror)typeAndInfo2[0]; if(!this.pluginContext.getTypeUtils().isAssignable(t1, t2)) { continue main; } } // t1 is assignable to all other types return this.unwildContextType((ReporterInfo)info, (DeclaredType)t1); } // report the error and ignore context type: compilation will fail infos.forEach(reporter -> { reporter.error("Inconsistent context types"); }); return null; }) .collect(Collectors.toSet()); // Filter types to remove duplicates: same type but with different parameters // SecurityContext can be casted to SecurityContext // => we need to remove wildcard because it simply doesn't compile: => GenericWebServerControllerConfigurerInfo webRouterConfigurerInfo = new GenericWebServerControllerConfigurerInfo( execution.getModuleElement(), webRouterConfigurerQName, webControllers, webInterceptorsConfigurers, webRoutesConfigurers, webRouterConfigurers, errorWebInterceptorsConfigurers, errorWebRoutesConfigurers, errorWebRouterConfigurers, contextTypes ); // Report route clash List routes = new ArrayList<>(); webControllers.stream().flatMap(controller -> Arrays.stream(controller.getRoutes())).forEach(routes::add); webRoutesConfigurers.stream().flatMap(configurer -> Arrays.stream(configurer.getRoutes())).forEach(routes::add); webRouterConfigurers.stream().flatMap(configurer -> Arrays.stream(configurer.getRoutes())).forEach(routes::add); for(Map.Entry> e : this.webRouteClashDetector.findDuplicates(routes).entrySet()) { e.getKey().error("Route " + e.getKey().getQualifiedName() + " is clashing with route(s):\n" + e.getValue().stream().map(route -> "- " + route.getQualifiedName()).collect(Collectors.joining("\n"))); } if(!webRouterConfigurerInfo.hasError() && (webRouterConfigurerInfo.getControllers().length > 0 || webRouterConfigurerInfo.getRoutesConfigurers().length > 0 || webRouterConfigurerInfo.getRouterConfigurers().length > 0)) { try { execution.createSourceFile(webRouterConfigurerInfo.getQualifiedName().getClassName(), execution.getElements().stream().toArray(Element[]::new), () -> { return webRouterConfigurerInfo.accept(this.webRouterConfigurerClassGenerator, new WebServerControllerConfigurerClassGenerationContext(this.pluginContext.getTypeUtils(), this.pluginContext.getElementUtils(), GenerationMode.CONFIGURER_CLASS)).toString(); }); } catch (IOException e) { throw new PluginExecutionException("Unable to generate web router configurer class " + webRouterConfigurerInfo.getQualifiedName().getClassName(), e); } if(webRouterConfigurerInfo.getControllers().length > 0 && this.pluginContext.getOptions().isOptionActivated(WebServerControllerConfigurerCompilerPlugin.OPTION_GENERATE_OPENAPI_DEFINITION, false)) { try { execution.createResourceFile("META-INF/inverno/web/server/" + webRouterConfigurerQName.getModuleQName().toString() + "/openapi.yml", execution.getElements().stream().toArray(Element[]::new), () -> { return webRouterConfigurerInfo.accept(this.openApiGenerator, new WebServerControllerConfigurerOpenApiGenerationContext(this.pluginContext.getTypeUtils(), this.pluginContext.getElementUtils(), this.pluginContext.getDocUtils(), WebServerControllerConfigurerOpenApiGenerationContext.GenerationMode.ROUTER_SPEC)).toString(); }); } catch (Exception e) { System.err.print("\n"); System.err.println("Error generating OpenApi specification for module : " + execution.getModuleQualifiedName()); if(this.pluginContext.getOptions().isDebug()) { e.printStackTrace(); } System.out.print("... "); } } } } /** *

* Scans the module for {@link WebRoute} annotated methods. *

* * @param execution * @param webRouteFactory */ private void processWebRoutes(PluginExecution execution, WebRouteInfoFactory webRouteFactory) { for(ExecutableElement routeElement : execution.getElementsAnnotatedWith(WebRoute.class)) { webRouteFactory.compileRoute(routeElement); } } /** *

* Scans the module for {@link WebController} annotated beans. *

* * @param execution * @param webRouteFactory * @return */ private List processWebControllers(PluginExecution execution, WebRouteInfoFactory webRouteFactory) { return Arrays.stream(execution.getBeans()) .map(bean -> { TypeElement beanElement = (TypeElement) this.pluginContext.getTypeUtils().asElement(bean.getType()); return this.typeHierarchyExtractor.extractTypeHierarchy(beanElement).stream() .map(element -> this.pluginContext.getElementUtils().getAllAnnotationMirrors(element).stream() .filter(annotation -> this.pluginContext.getTypeUtils().isSameType(annotation.getAnnotationType(), this.webControllerAnnotationType)) .findFirst() ) .filter(Optional::isPresent) .map(Optional::get) .findFirst() .map(webControllerAnnotation -> { String controllerRootPath = null; for(Entry value : this.pluginContext.getElementUtils().getElementValuesWithDefaults(webControllerAnnotation).entrySet()) { switch(value.getKey().getSimpleName().toString()) { case "path" : controllerRootPath = (String)value.getValue().getValue(); break; } } List webRoutes = webRouteFactory.compileControllerRoutes(bean); if(webRoutes.isEmpty()) { bean.warning("Ignoring web controller which does not define any route"); return null; } return new GenericServerWebControllerInfo(beanElement, bean.getQualifiedName(), bean, (DeclaredType)bean.getType(), controllerRootPath, webRoutes); }); }) .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.toList()); } /** *

* Scans the module for {@link WebInterceptorsConfigurer} beans. *

* * @param execution * @param webRouteFactory * @return */ private List processWebInterceptorsConfigurers(PluginExecution execution) { // Get web interceptors configurers return Arrays.stream(execution.getBeans()) .filter(bean -> this.pluginContext.getTypeUtils().isAssignable(bean.getType(), this.webInterceptorsConfigurerType)) .map(bean -> { TypeElement beanElement = (TypeElement) this.pluginContext.getTypeUtils().asElement(bean.getType()); // We want to include al web interceptors configurer // - raise a warning if it is defined in the module and not private if(bean.getQualifiedName().getModuleQName().equals(execution.getModuleQualifiedName()) && bean instanceof ModuleBeanInfo && ((ModuleBeanInfo)bean).getVisibility().equals(Bean.Visibility.PUBLIC)) { bean.warning("Web interceptors configurer bean should be declared with Visibility.PRIVATE to prevent side effects when the module is composed"); } List webInterceptorsConfigurerTypeArguments = ((DeclaredType)this.findSuperType(bean.getType(), this.webInterceptorsConfigurerType)).getTypeArguments(); TypeMirror contextType = !webInterceptorsConfigurerTypeArguments.isEmpty() ? webInterceptorsConfigurerTypeArguments.get(0) : this.exchangeContextType; return new GenericWebInterceptorsConfigurerInfo(beanElement, new WebConfigurerQualifiedName(bean.getQualifiedName(), this.pluginContext.getTypeUtils().asElement(this.pluginContext.getTypeUtils().erasure(bean.getType())).getSimpleName().toString()), bean, contextType); }) .filter(Objects::nonNull) .collect(Collectors.toList()); } /** *

* Scans the module for {@link WebRoutesConfigurer} beans. *

* * @param execution * @param webRouteFactory * @return */ private List processWebRoutesConfigurers(PluginExecution execution, WebRouteInfoFactory webRouteFactory) { // Get web interceptors configurers return Arrays.stream(execution.getBeans()) .filter(bean -> this.pluginContext.getTypeUtils().isAssignable(bean.getType(), this.webRoutesConfigurerType)) .map(bean -> { TypeElement beanElement = (TypeElement) this.pluginContext.getTypeUtils().asElement(bean.getType()); // We want to include al web router configurer // - raise a warning if it is defined in the module and not private // - raise a warning if it is defined without @WebRoutes Optional> webRoutes = this.pluginContext.getElementUtils().getAllAnnotationMirrors(beanElement).stream() .filter(annotation -> this.pluginContext.getTypeUtils().isSameType(annotation.getAnnotationType(), this.webRoutesAnnotationType)) .findFirst() .map(webRouterConfigurerAnnotation -> webRouteFactory.compileRouterRoutes(bean)); if(!webRoutes.isPresent()) { bean.warning("Unable to determine route clashes since no route is specified in @WebRoutes"); } if(bean.getQualifiedName().getModuleQName().equals(execution.getModuleQualifiedName()) && bean instanceof ModuleBeanInfo && ((ModuleBeanInfo)bean).getVisibility().equals(Bean.Visibility.PUBLIC)) { bean.warning("Web routes configurer bean should be declared with Visibility.PRIVATE to prevent side effects when the module is composed"); } List webRouterConfigurerTypeArguments = ((DeclaredType)this.findSuperType(bean.getType(), this.webRoutesConfigurerType)).getTypeArguments(); TypeMirror contextType = !webRouterConfigurerTypeArguments.isEmpty() ? webRouterConfigurerTypeArguments.get(0) : this.exchangeContextType; return new GenericWebRoutesConfigurerInfo(beanElement, new WebConfigurerQualifiedName(bean.getQualifiedName(), this.pluginContext.getTypeUtils().asElement(this.pluginContext.getTypeUtils().erasure(bean.getType())).getSimpleName().toString()), bean, webRoutes.orElse(List.of()), contextType); }) .filter(Objects::nonNull) .collect(Collectors.toList()); } /** *

* Scans the module for {@link WebRouterConfigurer} beans. *

* * @param execution * @param webRouteFactory * @return */ private List processWebRouterConfigurers(PluginExecution execution, WebRouteInfoFactory webRouteFactory) { // Get web router configurers return Arrays.stream(execution.getBeans()) .filter(bean -> this.pluginContext.getTypeUtils().isAssignable(bean.getType(), this.webRouterConfigurerType)) .map(bean -> { TypeElement beanElement = (TypeElement) this.pluginContext.getTypeUtils().asElement(bean.getType()); // We want to include al web router configurer // - raise a warning if it is defined in the module and not private // - raise a warning if it is defined without @WebRoutes Optional> webRoutes = this.pluginContext.getElementUtils().getAllAnnotationMirrors(beanElement).stream() .filter(annotation -> this.pluginContext.getTypeUtils().isSameType(annotation.getAnnotationType(), this.webRoutesAnnotationType)) .findFirst() .map(webRouterConfigurerAnnotation -> webRouteFactory.compileRouterRoutes(bean)); if(!webRoutes.isPresent()) { bean.warning("Can't determine route clashes since no route is specified in @WebRoutes"); } if(bean.getQualifiedName().getModuleQName().equals(execution.getModuleQualifiedName()) && bean instanceof ModuleBeanInfo && ((ModuleBeanInfo)bean).getVisibility().equals(Bean.Visibility.PUBLIC)) { bean.warning("Web Router configurer bean should be declared with Visibility.PRIVATE to prevent side effects when the module is composed"); } List webRouterConfigurerTypeArguments = ((DeclaredType)this.findSuperType(bean.getType(), this.webRouterConfigurerType)).getTypeArguments(); TypeMirror contextType = !webRouterConfigurerTypeArguments.isEmpty() ? webRouterConfigurerTypeArguments.get(0) : this.exchangeContextType; return new GenericWebRouterConfigurerInfo(beanElement, new WebConfigurerQualifiedName(bean.getQualifiedName(), this.pluginContext.getTypeUtils().asElement(this.pluginContext.getTypeUtils().erasure(bean.getType())).getSimpleName().toString()), bean, webRoutes.orElse(List.of()), contextType); }) .filter(Objects::nonNull) .collect(Collectors.toList()); } /** *

* Scans the module for {@link ErrorWebInterceptorsConfigurer} beans. *

* * @param execution * @param webRouteFactory * @return */ private List processErrorWebInterceptorsConfigurers(PluginExecution execution) { // Get web interceptors configurers return Arrays.stream(execution.getBeans()) .filter(bean -> this.pluginContext.getTypeUtils().isAssignable(bean.getType(), this.errorWebInterceptorsConfigurerType)) .map(bean -> { TypeElement beanElement = (TypeElement) this.pluginContext.getTypeUtils().asElement(bean.getType()); // We want to include all error web interceptors configurer // - raise a warning if it is defined in the module and not private if(bean.getQualifiedName().getModuleQName().equals(execution.getModuleQualifiedName()) && bean instanceof ModuleBeanInfo && ((ModuleBeanInfo)bean).getVisibility().equals(Bean.Visibility.PUBLIC)) { bean.warning("Error web interceptors configurer bean should be declared with Visibility.PRIVATE to prevent side effects when the module is composed"); } List errorWebInterceptorsConfigurerTypeArguments = ((DeclaredType)this.findSuperType(bean.getType(), this.errorWebInterceptorsConfigurerType)).getTypeArguments(); TypeMirror contextType = !errorWebInterceptorsConfigurerTypeArguments.isEmpty() ? errorWebInterceptorsConfigurerTypeArguments.get(0) : this.exchangeContextType; return new GenericErrorWebInterceptorsConfigurerInfo(beanElement, new WebConfigurerQualifiedName(bean.getQualifiedName(), this.pluginContext.getTypeUtils().asElement(this.pluginContext.getTypeUtils().erasure(bean.getType())).getSimpleName().toString()), bean, contextType); }) .filter(Objects::nonNull) .collect(Collectors.toList()); } /** *

* Scans the module for {@link ErrorWebRoutesConfigurer} beans. *

* * @param execution * @param webRouteFactory * @return */ private List processErrorWebRoutesConfigurers(PluginExecution execution) { // Get web interceptors configurers return Arrays.stream(execution.getBeans()) .filter(bean -> this.pluginContext.getTypeUtils().isAssignable(bean.getType(), this.errorWebRoutesConfigurerType)) .map(bean -> { TypeElement beanElement = (TypeElement) this.pluginContext.getTypeUtils().asElement(bean.getType()); // We want to include all error web routes configurer // - raise a warning if it is defined in the module and not private if(bean.getQualifiedName().getModuleQName().equals(execution.getModuleQualifiedName()) && bean instanceof ModuleBeanInfo && ((ModuleBeanInfo)bean).getVisibility().equals(Bean.Visibility.PUBLIC)) { bean.warning("Error web routes configurer bean should be declared with Visibility.PRIVATE to prevent side effects when the module is composed"); } List errorWebRouterConfigurerTypeArguments = ((DeclaredType)this.findSuperType(bean.getType(), this.errorWebRoutesConfigurerType)).getTypeArguments(); TypeMirror contextType = !errorWebRouterConfigurerTypeArguments.isEmpty() ? errorWebRouterConfigurerTypeArguments.get(0) : this.exchangeContextType; return new GenericErrorWebRoutesConfigurerInfo(beanElement, new WebConfigurerQualifiedName(bean.getQualifiedName(), this.pluginContext.getTypeUtils().asElement(this.pluginContext.getTypeUtils().erasure(bean.getType())).getSimpleName().toString()), bean, contextType); }) .filter(Objects::nonNull) .collect(Collectors.toList()); } /** *

* Scans the module for {@link ErrorWebRouterConfigurer} beans. *

* * @param execution * @param webRouteFactory * @return */ private List processErrorWebRouterConfigurers(PluginExecution execution) { // Get web router configurers return Arrays.stream(execution.getBeans()) .filter(bean -> this.pluginContext.getTypeUtils().isAssignable(bean.getType(), this.errorWebRouterConfigurerType)) .map(bean -> { TypeElement beanElement = (TypeElement) this.pluginContext.getTypeUtils().asElement(bean.getType()); // We want to include all error web router configurer // - raise a warning if it is defined in the module and not private if(bean.getQualifiedName().getModuleQName().equals(execution.getModuleQualifiedName()) && bean instanceof ModuleBeanInfo && ((ModuleBeanInfo)bean).getVisibility().equals(Bean.Visibility.PUBLIC)) { bean.warning("Error web router configurer bean should be declared with Visibility.PRIVATE to prevent side effects when the module is composed"); } List errorWebRouterConfigurerTypeArguments = ((DeclaredType)this.findSuperType(bean.getType(), this.errorWebRouterConfigurerType)).getTypeArguments(); TypeMirror contextType = !errorWebRouterConfigurerTypeArguments.isEmpty() ? errorWebRouterConfigurerTypeArguments.get(0) : this.exchangeContextType; return new GenericErrorWebRouterConfigurerInfo(beanElement, new WebConfigurerQualifiedName(bean.getQualifiedName(), this.pluginContext.getTypeUtils().asElement(this.pluginContext.getTypeUtils().erasure(bean.getType())).getSimpleName().toString()), bean, contextType); }) .filter(Objects::nonNull) .collect(Collectors.toList()); } /** *

* Finds the super types matching the specified erased super type. *

* * @param type the type where to find the super type * @param erasedSuperType the erased super type to find * * @return a type or null */ private TypeMirror findSuperType(TypeMirror type, TypeMirror erasedSuperType) { for(TypeMirror superType : this.pluginContext.getTypeUtils().directSupertypes(type)) { if(this.pluginContext.getTypeUtils().isSameType(this.pluginContext.getTypeUtils().erasure(superType), erasedSuperType)) { return superType; } else { TypeMirror result = this.findSuperType(superType, erasedSuperType); if(result != null) { return result; } } } return null; } /** *

* Returns all the unique super types of the specified type, Object excluded. *

* * @param type a type * * @return a set of types */ private Set getAllSuperTypes(TypeMirror type) { Set result = new TreeSet<>( (t1,t2) -> { if(this.pluginContext.getTypeUtils().isSameType(t1, t2)) { return 0; } return t1.toString().compareTo(t2.toString()); }); for(TypeMirror superType : this.pluginContext.getTypeUtils().directSupertypes(type)) { if(!this.pluginContext.getTypeUtils().isSameType(superType, this.objectType)) { result.add(superType); result.addAll(this.getAllSuperTypes(superType)); } } return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy