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

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

There is a newer version: 4.6.5
Show newest version
/*
 * 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.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.order.OrderUtil;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.SupplierUtil;
import io.micronaut.http.HttpAttributes;
import io.micronaut.http.HttpMethod;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Filter;
import io.micronaut.http.annotation.FilterMatcher;
import io.micronaut.http.filter.FilterPatternStyle;
import io.micronaut.http.filter.FilterRunner;
import io.micronaut.http.filter.GenericHttpFilter;
import io.micronaut.http.filter.HttpServerFilterResolver;
import io.micronaut.http.uri.UriMatchTemplate;
import io.micronaut.web.router.exceptions.DuplicateRouteException;
import io.micronaut.web.router.exceptions.RoutingException;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;

/**
 * 

The default {@link Router} implementation. This implementation does not perform any additional caching of * route discovery.

* * @author Graeme Rocher * @since 1.0 */ @Singleton public class DefaultRouter implements Router, HttpServerFilterResolver> { private static final UriRouteInfo[] EMPTY = new UriRouteInfo[0]; private final EnumMap[]> methodRoutesByMethod; private final Map[]> allRoutesByMethod; private final StatusRouteInfo[] statusRoutes; private final ErrorRouteInfo[] errorRoutes; private final Set exposedPorts; @Nullable private Set ports; private final List alwaysMatchesFilterRoutes; private final List preconditionFilterRoutes; private final List preMatchingAlwaysMatchesFilterRoutes; private final List preMatchingPreconditionFilterRoutes; // ArrayList to avoid interface checkcast private final Supplier> alwaysMatchesHttpFilters; private final Supplier> preMatchingAlwaysMatchesHttpFilters; /** * Construct a new router for the given route builders. * * @param builders The builders */ public DefaultRouter(RouteBuilder... builders) { this(Arrays.asList(builders)); } /** * Construct a new router for the given route builders. * * @param builders The builders */ @Inject public DefaultRouter(Collection builders) { Set exposedPorts = new HashSet<>(5); Map>> customRoutesByMethod = new HashMap<>(); EnumMap>> routesByMethod = new EnumMap<>(HttpMethod.class); Set> statusRoutes = new LinkedHashSet<>(); Set> errorRoutes = new LinkedHashSet<>(); alwaysMatchesFilterRoutes = new ArrayList<>(20); preconditionFilterRoutes = new ArrayList<>(20); preMatchingAlwaysMatchesFilterRoutes = new ArrayList<>(10); preMatchingPreconditionFilterRoutes = new ArrayList<>(10); for (RouteBuilder builder : builders) { List constructedRoutes = builder.getUriRoutes(); for (UriRoute route : constructedRoutes) { HttpMethod httpMethod = route.getHttpMethod(); UriRouteInfo uriRouteInfo = route.toRouteInfo(); if (httpMethod == HttpMethod.CUSTOM) { String key = route.getHttpMethodName(); customRoutesByMethod.computeIfAbsent(key, x -> new ArrayList<>()).add(uriRouteInfo); } else { routesByMethod.computeIfAbsent(httpMethod, x -> new ArrayList<>()).add(uriRouteInfo); } } for (StatusRoute statusRoute : builder.getStatusRoutes()) { StatusRouteInfo routeInfo = statusRoute.toRouteInfo(); if (statusRoutes.contains(routeInfo)) { final StatusRouteInfo existing = statusRoutes.stream().filter(r -> r.equals(routeInfo)).findFirst().orElse(null); throw new RoutingException("Attempted to register multiple local routes for http status [" + statusRoute.statusCode() + "]. New route: " + statusRoute + ". Existing: " + existing); } statusRoutes.add(routeInfo); } for (ErrorRoute errorRoute : builder.getErrorRoutes()) { ErrorRouteInfo routeInfo = errorRoute.toRouteInfo(); if (errorRoutes.contains(routeInfo)) { final ErrorRouteInfo existing = errorRoutes.stream().filter(r -> r.equals(routeInfo)).findFirst().orElse(null); throw new RoutingException("Attempted to register multiple local routes for error [" + errorRoute.exceptionType().getSimpleName() + "]. New route: " + errorRoute + ". Existing: " + existing); } errorRoutes.add(routeInfo); } for (FilterRoute filterRoute : builder.getFilterRoutes()) { if (filterRoute.isPreMatching()) { if (isMatchesAll(filterRoute)) { preMatchingAlwaysMatchesFilterRoutes.add(filterRoute); } else { preMatchingPreconditionFilterRoutes.add(filterRoute); } } else if (isMatchesAll(filterRoute)) { alwaysMatchesFilterRoutes.add(filterRoute); } else { preconditionFilterRoutes.add(filterRoute); } } exposedPorts.addAll(builder.getExposedPorts()); } if (CollectionUtils.isNotEmpty(exposedPorts)) { this.exposedPorts = exposedPorts; } else { this.exposedPorts = Collections.emptySet(); } EnumMap[]> methodMap = new EnumMap<>(HttpMethod.class); Map[]> customMethodMap = CollectionUtils.newHashMap(routesByMethod.size() + customRoutesByMethod.size()); for (Map.Entry>> e : routesByMethod.entrySet()) { UriRouteInfo[] values = finalizeRoutes(e.getValue()); methodMap.put(e.getKey(), values); customMethodMap.put(e.getKey().name(), values); } for (Map.Entry>> e : customRoutesByMethod.entrySet()) { customMethodMap.put(e.getKey(), finalizeRoutes(e.getValue())); } this.methodRoutesByMethod = methodMap; this.allRoutesByMethod = customMethodMap; this.statusRoutes = statusRoutes.toArray(StatusRouteInfo[]::new); this.errorRoutes = errorRoutes.toArray(ErrorRouteInfo[]::new); this.alwaysMatchesHttpFilters = SupplierUtil.memoized(() -> { if (alwaysMatchesFilterRoutes.isEmpty()) { return new ArrayList<>(0); } ArrayList httpFilters = new ArrayList<>(alwaysMatchesFilterRoutes.size()); for (FilterRoute filterRoute : alwaysMatchesFilterRoutes) { httpFilters.add(filterRoute.getFilter()); } FilterRunner.sort(httpFilters); return httpFilters; }); this.preMatchingAlwaysMatchesHttpFilters = SupplierUtil.memoized(() -> { if (preMatchingAlwaysMatchesFilterRoutes.isEmpty()) { return new ArrayList<>(0); } ArrayList httpFilters = new ArrayList<>(preMatchingAlwaysMatchesFilterRoutes.size()); for (FilterRoute filterRoute : preMatchingAlwaysMatchesFilterRoutes) { httpFilters.add(filterRoute.getFilter()); } FilterRunner.sort(httpFilters); return httpFilters; }); } private boolean isMatchesAll(FilterRoute filterRoute) { if (filterRoute.getAnnotationMetadata().hasStereotype(FilterMatcher.NAME)) { return false; } if (filterRoute.hasMethods()) { return false; } if (filterRoute.hasPatterns()) { for (String pattern : filterRoute.getPatterns()) { if (!Filter.MATCH_ALL_PATTERN.equals(pattern)) { return false; } } } return true; } @Override public Set getExposedPorts() { return exposedPorts; } @Override public void applyDefaultPorts(List ports) { this.ports = new HashSet<>(ports); } @NonNull @Override public Stream> find(@NonNull HttpRequest request, @NonNull CharSequence uri) { return this.toMatches(uri.toString(), findInternal(request)).stream(); } @NonNull @Override public Stream> find(@NonNull HttpRequest request) { return this.toMatches(request.getPath(), findInternal(request)).stream(); } @NonNull @Override public Stream> find(@NonNull HttpMethod httpMethod, @NonNull CharSequence uri, @Nullable HttpRequest context) { return this.toMatches( uri.toString(), allRoutesByMethod.getOrDefault(httpMethod.name(), EMPTY) ).stream(); } @NonNull @Override public Stream> uriRoutes() { return Stream.concat( allRoutesByMethod.values().stream().flatMap(Arrays::stream), allRoutesByMethod.values().stream().flatMap(Arrays::stream) ); } @Override public UriRouteMatch findClosest(@NonNull HttpRequest request) throws DuplicateRouteException { List> routes = findInternal(request); if (routes.isEmpty()) { return null; } String path = request.getPath(); if (routes.size() == 1) { Object o = routes.iterator().next(); // avoid type pollution perf issues UriRouteInfo next = o instanceof DefaultUrlRouteInfo def ? def : (UriRouteInfo) o; return (UriRouteMatch) next.tryMatch(path); } List> uriRoutes = new ArrayList<>(routes.size()); for (UriRouteInfo route : routes) { UriRouteMatch match = route.tryMatch(path); if (match != null) { uriRoutes.add(match); } } if (uriRoutes.size() == 1) { return uriRoutes.get(0); } uriRoutes = resolveAmbiguity(request, uriRoutes); if (uriRoutes.size() > 1) { throw new DuplicateRouteException(path, (List) uriRoutes); } else if (uriRoutes.size() == 1) { return uriRoutes.get(0); } return null; } @NonNull @Override public List> findAllClosest(@NonNull HttpRequest request) { List> routes = findInternal(request); if (routes.isEmpty()) { return Collections.emptyList(); } List> uriRoutes = toMatches(request.getPath(), routes); if (uriRoutes.size() == 1) { return uriRoutes; } return resolveAmbiguity(request, uriRoutes); } private List> resolveAmbiguity(HttpRequest request, List> uriRoutes) { // if there are multiple routes, try to resolve the ambiguity final Collection acceptedProducedTypes = request.accept(); if (CollectionUtils.isNotEmpty(acceptedProducedTypes)) { // take the highest priority accepted type final MediaType mediaType = acceptedProducedTypes.iterator().next(); var mostSpecific = new ArrayList>(uriRoutes.size()); for (UriRouteMatch routeMatch : uriRoutes) { if (routeMatch.getRouteInfo().explicitlyProduces(mediaType)) { mostSpecific.add(routeMatch); } } if (!mostSpecific.isEmpty()) { uriRoutes = mostSpecific; } } boolean permitsBody = request.getMethod().permitsRequestBody(); int routeCount = uriRoutes.size(); if (routeCount > 1 && permitsBody) { final MediaType contentType = request.getContentType().orElse(MediaType.ALL_TYPE); var explicitlyConsumedRoutes = new ArrayList>(routeCount); var consumesRoutes = new ArrayList>(routeCount); for (UriRouteMatch match : uriRoutes) { if (match.getRouteInfo().explicitlyConsumes(contentType)) { explicitlyConsumedRoutes.add(match); } if (explicitlyConsumedRoutes.isEmpty()) { consumesRoutes.add(match); } } uriRoutes = explicitlyConsumedRoutes.isEmpty() ? consumesRoutes : explicitlyConsumedRoutes; } /* * Any changes to the logic below may also need changes to {@link io.micronaut.http.uri.UriTemplate#compareTo(UriTemplate)} */ routeCount = uriRoutes.size(); if (routeCount > 1) { long variableCount = 0; long rawLength = 0; var closestMatches = new ArrayList>(routeCount); for (int i = 0; i < routeCount; i++) { UriRouteMatch match = uriRoutes.get(i); UriMatchTemplate template = match.getRouteInfo().getUriMatchTemplate(); long variable = template.getPathVariableSegmentCount(); long raw = template.getRawSegmentLength(); if (i == 0) { variableCount = variable; rawLength = raw; } if (variable > variableCount || raw < rawLength) { break; } closestMatches.add(match); } uriRoutes = closestMatches; } return uriRoutes; } private List> toMatches(String path, List> routes) { if (routes.size() == 1) { UriRouteMatch match = routes.iterator().next().tryMatch(path); if (match != null) { return List.of(match); } return List.of(); } var uriRoutes = new ArrayList>(routes.size()); for (UriRouteInfo route : routes) { UriRouteMatch match = route.tryMatch(path); if (match != null) { uriRoutes.add(match); } } return uriRoutes; } private List> toMatches(String path, UriRouteInfo[] routes) { if (routes.length == 1) { UriRouteMatch match = routes[0].tryMatch(path); if (match != null) { return List.of(match); } return List.of(); } var uriRoutes = new ArrayList>(routes.length); for (UriRouteInfo route : routes) { UriRouteMatch match = route.tryMatch(path); if (match != null) { uriRoutes.add(match); } } return uriRoutes; } @NonNull @Override public Optional> route(@NonNull HttpMethod httpMethod, @NonNull CharSequence uri) { for (UriRouteInfo uriRouteInfo : methodRoutesByMethod.getOrDefault(httpMethod, EMPTY)) { Optional> match = uriRouteInfo.match(uri.toString()); if (match.isPresent()) { return (Optional) match; } } return Optional.empty(); } @Override public Optional> route(@NonNull HttpStatus status) { for (StatusRouteInfo statusRouteInfo : statusRoutes) { if (statusRouteInfo.originatingType() == null) { Optional> match = statusRouteInfo.match(status); if (match.isPresent()) { return (Optional) match; } } } return Optional.empty(); } @Override public Optional> route(@NonNull Class originatingClass, @NonNull HttpStatus status) { for (StatusRouteInfo statusRouteInfo : statusRoutes) { Optional> match = statusRouteInfo.match(originatingClass, status); if (match.isPresent()) { return (Optional) match; } } return Optional.empty(); } @Override public Optional> route(@NonNull Class originatingClass, @NonNull Throwable error) { var matchedRoutes = new ArrayList>(); for (ErrorRouteInfo errorRouteInfo : errorRoutes) { Optional match = errorRouteInfo.match(originatingClass, error); match.ifPresent(m -> matchedRoutes.add((RouteMatch) m) ); } return findRouteMatch(matchedRoutes, error); } @Override public Optional> findErrorRoute( @NonNull Class originatingClass, @NonNull Throwable error, HttpRequest request) { return findErrorRouteInternal(originatingClass, error, request); } private Optional> findErrorRouteInternal( @Nullable Class originatingClass, @NonNull Throwable error, HttpRequest request) { Collection accept = request.accept(); final boolean hasAcceptHeader = CollectionUtils.isNotEmpty(accept); if (hasAcceptHeader) { var matchedRoutes = new ArrayList>(); for (ErrorRouteInfo errorRoute : errorRoutes) { if (!errorRoute.doesProduce(accept)) { continue; } if (!errorRoute.matching(request)) { continue; } @SuppressWarnings("unchecked") final var match = (RouteMatch) errorRoute.match(originatingClass, error).orElse(null); if (match != null) { matchedRoutes.add(match); } } return findRouteMatch(matchedRoutes, error); } else { var producesAllMatchedRoutes = new ArrayList>(errorRoutes.length); var producesSpecificMatchedRoutes = new ArrayList>(errorRoutes.length); for (ErrorRouteInfo errorRouteInfo : errorRoutes) { if (!errorRouteInfo.matching(request)) { continue; } @SuppressWarnings("unchecked") final RouteMatch match = (RouteMatch) errorRouteInfo .match(originatingClass, error).orElse(null); if (match != null) { final List produces = match.getRouteInfo().getProduces(); if (CollectionUtils.isEmpty(produces) || produces.contains(MediaType.ALL_TYPE)) { producesAllMatchedRoutes.add(match); } else { producesSpecificMatchedRoutes.add(match); } } } if (producesAllMatchedRoutes.isEmpty()) { return findRouteMatch(producesSpecificMatchedRoutes, error); } return findRouteMatch(producesAllMatchedRoutes, error); } } @Override public Optional> findErrorRoute(@NonNull Throwable error, HttpRequest request) { return findErrorRouteInternal(null, error, request); } @Override public Optional> findStatusRoute( @NonNull Class originatingClass, @NonNull HttpStatus status, HttpRequest request) { return findStatusInternal(originatingClass, status.getCode(), request); } @Override public Optional> findStatusRoute(@NonNull HttpStatus status, HttpRequest request) { return findStatusInternal(null, status.getCode(), request); } @Override public Optional> findStatusRoute(@NonNull Class originatingClass, int statusCode, HttpRequest request) { return findStatusInternal(originatingClass, statusCode, request); } @Override public Optional> findStatusRoute(int statusCode, HttpRequest request) { return findStatusInternal(null, statusCode, request); } private Optional> findStatusInternal(@Nullable Class originatingClass, int status, HttpRequest request) { Collection accept = request.accept(); final boolean hasAcceptHeader = CollectionUtils.isNotEmpty(accept); if (hasAcceptHeader) { for (StatusRouteInfo statusRouteInfo : statusRoutes) { if (!statusRouteInfo.doesProduce(accept)) { continue; } if (!statusRouteInfo.matching(request)) { continue; } @SuppressWarnings("unchecked") final RouteMatch match = (RouteMatch) statusRouteInfo .match(originatingClass, status).orElse(null); if (match != null) { return Optional.of(match); } } } else { RouteMatch firstMatch = null; for (StatusRouteInfo statusRouteInfo : statusRoutes) { if (!statusRouteInfo.matching(request)) { continue; } @SuppressWarnings("unchecked") final RouteMatch match = (RouteMatch) statusRouteInfo .match(originatingClass, status).orElse(null); if (match != null) { final List produces = match.getRouteInfo().getProduces(); if (CollectionUtils.isEmpty(produces) || produces.contains(MediaType.ALL_TYPE)) { return Optional.of(match); } else if (firstMatch == null) { firstMatch = match; } } } return Optional.ofNullable(firstMatch); } return Optional.empty(); } @Override public Optional> route(@NonNull Throwable error) { var matchedRoutes = new ArrayList>(); for (ErrorRouteInfo errorRouteInfo : errorRoutes) { if (errorRouteInfo.originatingType() == null) { Optional match = errorRouteInfo.match(error); match.ifPresent(m -> matchedRoutes.add((RouteMatch) m)); } } return findRouteMatch(matchedRoutes, error); } @NonNull @Override public List findFilters(@NonNull HttpRequest request) { if (preconditionFilterRoutes.isEmpty()) { // for perf, this needs to be placed in an ArrayList variable first @SuppressWarnings("UnnecessaryLocalVariable") ArrayList always = alwaysMatchesHttpFilters.get(); return always; } var httpFilters = new ArrayList(alwaysMatchesFilterRoutes.size() + preconditionFilterRoutes.size()); httpFilters.addAll(alwaysMatchesHttpFilters.get()); var routeMatch = (RouteMatch) request.getAttribute(HttpAttributes.ROUTE_MATCH) .filter(o -> o instanceof RouteMatch) .orElse(null); HttpMethod method = request.getMethod(); String path = request.getPath(); for (FilterRoute filterRoute : preconditionFilterRoutes) { if (routeMatch != null) { if (!matchesFilterMatcher(filterRoute, routeMatch)) { continue; } } filterRoute.match(method, path).ifPresent(httpFilters::add); } FilterRunner.sort(httpFilters); return Collections.unmodifiableList(httpFilters); } @NonNull @Override public List findFilters(@NonNull HttpRequest request, @Nullable RouteMatch routeMatch) { if (preconditionFilterRoutes.isEmpty()) { // for perf, this needs to be placed in an ArrayList variable first @SuppressWarnings("UnnecessaryLocalVariable") ArrayList always = alwaysMatchesHttpFilters.get(); return always; } var httpFilters = new ArrayList(alwaysMatchesFilterRoutes.size() + preconditionFilterRoutes.size()); httpFilters.addAll(alwaysMatchesHttpFilters.get()); HttpMethod method = request.getMethod(); String path = request.getPath(); for (FilterRoute filterRoute : preconditionFilterRoutes) { if (routeMatch != null && !matchesFilterMatcher(filterRoute, routeMatch)) { continue; } filterRoute.match(method, path).ifPresent(httpFilters::add); } FilterRunner.sort(httpFilters); return Collections.unmodifiableList(httpFilters); } @NonNull @Override public List findPreMatchingFilters(@NonNull HttpRequest request) { if (preMatchingPreconditionFilterRoutes.isEmpty()) { // for perf, this needs to be placed in an ArrayList variable first @SuppressWarnings("UnnecessaryLocalVariable") ArrayList always = preMatchingAlwaysMatchesHttpFilters.get(); return always; } var httpFilters = new ArrayList(preMatchingAlwaysMatchesFilterRoutes.size() + preMatchingPreconditionFilterRoutes.size()); httpFilters.addAll(preMatchingAlwaysMatchesHttpFilters.get()); HttpMethod method = request.getMethod(); String path = request.getPath(); for (FilterRoute filterRoute : preMatchingPreconditionFilterRoutes) { filterRoute.match(method, path).ifPresent(httpFilters::add); } FilterRunner.sort(httpFilters); return Collections.unmodifiableList(httpFilters); } @SuppressWarnings("unchecked") @NonNull @Override public Stream> findAny(@NonNull CharSequence uri, @Nullable HttpRequest request) { var matchedRoutes = new ArrayList>(5); final String uriStr = uri.toString(); for (UriRouteInfo[] routes : allRoutesByMethod.values()) { for (UriRouteInfo route : routes) { if (request != null) { if (shouldSkipForPort(request, route)) { continue; } if (!route.matching(request)) { continue; } } UriRouteMatch match = route.tryMatch(uriStr); if (match != null) { matchedRoutes.add(match); } } } return matchedRoutes.stream(); } @Override @NonNull public List> findAny(@NonNull HttpRequest request) { String path = request.getPath(); var matchedRoutes = new ArrayList>(5); for (UriRouteInfo[] routes : allRoutesByMethod.values()) { for (UriRouteInfo route : routes) { if (shouldSkipForPort(request, route)) { continue; } if (!route.matching(request)) { continue; } UriRouteMatch match = route.tryMatch(path); if (match != null) { matchedRoutes.add(match); } } } return matchedRoutes; } private List> findInternal(HttpRequest request) { HttpMethod httpMethod = request.getMethod(); boolean permitsBody = httpMethod.permitsRequestBody(); Collection acceptedProducedTypes = null; MediaType contentType = null; UriRouteInfo[] routes = httpMethod == HttpMethod.CUSTOM ? allRoutesByMethod.getOrDefault(request.getMethodName(), EMPTY) : methodRoutesByMethod.getOrDefault(httpMethod, EMPTY); if (routes.length == 0) { return Collections.emptyList(); } var result = new ArrayList>(routes.length); for (UriRouteInfo route : routes) { if (shouldSkipForPort(request, route)) { continue; } if (permitsBody) { if (!route.isPermitsRequestBody()) { continue; } if (!route.consumesAll()) { if (contentType == null) { contentType = request.getContentType().orElse(null); } if (!route.doesConsume(contentType)) { continue; } } } if (!route.producesAll()) { if (acceptedProducedTypes == null) { acceptedProducedTypes = request.accept(); } if (!route.doesProduce(acceptedProducedTypes)) { continue; } } if (!route.matching(request)) { continue; } result.add(route); } return result; } private boolean shouldSkipForPort(HttpRequest request, UriRouteInfo route) { if (ports == null || route.getPort() != null) { return false; } return !ports.contains(request.getServerAddress().getPort()); } private UriRouteInfo[] finalizeRoutes(List> routes) { Collections.sort(routes); return routes.toArray(EMPTY); } private Optional> findRouteMatch(List> matchedRoutes, Throwable error) { if (matchedRoutes.size() == 1) { return matchedRoutes.stream().findFirst(); } else if (matchedRoutes.size() > 1) { int minCount = Integer.MAX_VALUE; Supplier>> hierarchySupplier = () -> ClassUtils.resolveHierarchy(error.getClass()); Optional> match = Optional.empty(); Class errorClass = error.getClass(); for (RouteMatch errorMatch : matchedRoutes) { ErrorRouteInfo routeInfo = (ErrorRouteInfo) errorMatch.getRouteInfo(); Class exceptionType = routeInfo.exceptionType(); if (exceptionType.equals(errorClass)) { match = Optional.of(errorMatch); break; } else { List> hierarchy = hierarchySupplier.get(); //measures the distance in the hierarchy from the error and the route error type int index = hierarchy.indexOf(exceptionType); //the class closest in the hierarchy should be chosen if (index > -1 && index < minCount) { minCount = index; match = Optional.of(errorMatch); } } } return match; } return Optional.empty(); } @Override public List resolveFilterEntries(RouteMatch routeMatch) { if (preconditionFilterRoutes.isEmpty()) { return new ArrayList<>(alwaysMatchesFilterRoutes); } List filterEntries = new ArrayList<>(alwaysMatchesFilterRoutes.size() + preconditionFilterRoutes.size()); filterEntries.addAll(alwaysMatchesFilterRoutes); for (FilterRoute filterRoute : preconditionFilterRoutes) { if (!matchesFilterMatcher(filterRoute, routeMatch)) { filterEntries.add(filterRoute); } } filterEntries.sort(OrderUtil.COMPARATOR); return Collections.unmodifiableList(filterEntries); } @Override public List resolveFilters(HttpRequest request, List filterEntries) { var httpFilters = new ArrayList(filterEntries.size()); for (FilterEntry entry : filterEntries) { if (entry.hasMethods() && !entry.getFilterMethods().contains(request.getMethod())) { continue; } if (entry.hasPatterns()) { String path = request.getPath(); String[] patterns = entry.getPatterns(); FilterPatternStyle patternStyle = entry.getAnnotationMetadata() .enumValue("patternStyle", FilterPatternStyle.class) .orElse(FilterPatternStyle.ANT); boolean matches = true; for (String pattern : patterns) { if (!matches) { break; } matches = Filter.MATCH_ALL_PATTERN.equals(pattern) || patternStyle.getPathMatcher().matches(pattern, path); } if (!matches) { continue; } } httpFilters.add(entry.getFilter()); } httpFilters.sort(OrderUtil.COMPARATOR); return Collections.unmodifiableList(httpFilters); } private boolean matchesFilterMatcher(FilterRoute filterRoute, RouteMatch context) { String matchingAnnotation = filterRoute.findMatchingAnnotation(); if (matchingAnnotation == null) { return true; } return context.getRouteInfo().getAnnotationMetadata().hasStereotype(matchingAnnotation); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy