Merge branch '4.0.x'
This commit is contained in:
@@ -16,24 +16,30 @@
|
||||
|
||||
package org.springframework.cloud.gateway.actuate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
|
||||
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
|
||||
import org.springframework.cloud.gateway.filter.FilterDefinition;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
|
||||
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
|
||||
@@ -42,6 +48,9 @@ import org.springframework.cloud.gateway.support.NotFoundException;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -51,6 +60,8 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
@@ -76,16 +87,77 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
|
||||
protected ApplicationEventPublisher publisher;
|
||||
|
||||
protected WebEndpointProperties webEndpointProperties;
|
||||
|
||||
private final SimpleMetadataReaderFactory simpleMetadataReaderFactory = new SimpleMetadataReaderFactory();
|
||||
|
||||
@Deprecated
|
||||
public AbstractGatewayControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator,
|
||||
List<GlobalFilter> globalFilters, List<GatewayFilterFactory> gatewayFilters,
|
||||
List<RoutePredicateFactory> routePredicates, RouteDefinitionWriter routeDefinitionWriter,
|
||||
RouteLocator routeLocator) {
|
||||
this(routeDefinitionLocator, globalFilters, gatewayFilters, routePredicates,
|
||||
routeDefinitionWriter, routeLocator, new WebEndpointProperties());
|
||||
}
|
||||
|
||||
public AbstractGatewayControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator,
|
||||
List<GlobalFilter> globalFilters, List<GatewayFilterFactory> gatewayFilters,
|
||||
List<RoutePredicateFactory> routePredicates, RouteDefinitionWriter routeDefinitionWriter,
|
||||
RouteLocator routeLocator, WebEndpointProperties webEndpointProperties) {
|
||||
this.routeDefinitionLocator = routeDefinitionLocator;
|
||||
this.globalFilters = globalFilters;
|
||||
this.GatewayFilters = gatewayFilters;
|
||||
this.routePredicates = routePredicates;
|
||||
this.routeDefinitionWriter = routeDefinitionWriter;
|
||||
this.routeLocator = routeLocator;
|
||||
this.webEndpointProperties = webEndpointProperties;
|
||||
}
|
||||
|
||||
@GetMapping("/")
|
||||
Mono<List<GatewayEndpointInfo>> getEndpoints() {
|
||||
List<GatewayEndpointInfo> endpoints = mergeEndpoints(
|
||||
getAvailableEndpointsForClass(AbstractGatewayControllerEndpoint.class.getName()),
|
||||
getAvailableEndpointsForClass(GatewayControllerEndpoint.class.getName()));
|
||||
|
||||
return Flux.fromIterable(endpoints).map(p -> p)
|
||||
.flatMap(path -> this.routeLocator.getRoutes().map(r -> generateHref(r, path)).distinct().collectList()
|
||||
.flatMapMany(Flux::fromIterable))
|
||||
.distinct() // Ensure overall uniqueness
|
||||
.collectList();
|
||||
}
|
||||
|
||||
private List<GatewayEndpointInfo> mergeEndpoints(List<GatewayEndpointInfo> listA,
|
||||
List<GatewayEndpointInfo> listB) {
|
||||
Map<String, List<String>> mergedMap = new HashMap<>();
|
||||
|
||||
Stream.concat(listA.stream(), listB.stream()).forEach(e -> mergedMap
|
||||
.computeIfAbsent(e.getHref(), k -> new ArrayList<>()).addAll(Arrays.asList(e.getMethods())));
|
||||
|
||||
return mergedMap.entrySet().stream().map(entry -> new GatewayEndpointInfo(entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<GatewayEndpointInfo> getAvailableEndpointsForClass(String className) {
|
||||
try {
|
||||
MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader(className);
|
||||
Set<MethodMetadata> annotatedMethods = metadataReader.getAnnotationMetadata()
|
||||
.getAnnotatedMethods(RequestMapping.class.getName());
|
||||
|
||||
String gatewayActuatorPath = webEndpointProperties.getBasePath() + "/gateway";
|
||||
return annotatedMethods.stream().map(method -> new GatewayEndpointInfo(gatewayActuatorPath
|
||||
+ ((String[]) method.getAnnotationAttributes(RequestMapping.class.getName()).get("path"))[0],
|
||||
((RequestMethod[]) method.getAnnotationAttributes(RequestMapping.class.getName()).get("method"))[0]
|
||||
.name()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
catch (IOException exception) {
|
||||
log.warn(exception.getMessage());
|
||||
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private GatewayEndpointInfo generateHref(Route r, GatewayEndpointInfo path) {
|
||||
return new GatewayEndpointInfo(path.getHref().replace("{id}", r.getId()), Arrays.asList(path.getMethods()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.Map;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
@@ -47,9 +48,10 @@ public class GatewayControllerEndpoint extends AbstractGatewayControllerEndpoint
|
||||
|
||||
public GatewayControllerEndpoint(List<GlobalFilter> globalFilters, List<GatewayFilterFactory> gatewayFilters,
|
||||
List<RoutePredicateFactory> routePredicates, RouteDefinitionWriter routeDefinitionWriter,
|
||||
RouteLocator routeLocator, RouteDefinitionLocator routeDefinitionLocator) {
|
||||
super(routeDefinitionLocator, globalFilters, gatewayFilters, routePredicates, routeDefinitionWriter,
|
||||
routeLocator);
|
||||
RouteLocator routeLocator, RouteDefinitionLocator routeDefinitionLocator,
|
||||
WebEndpointProperties webEndpointProperties) {
|
||||
super(routeDefinitionLocator, globalFilters, gatewayFilters, routePredicates,
|
||||
routeDefinitionWriter, routeLocator, webEndpointProperties);
|
||||
}
|
||||
|
||||
@GetMapping("/routedefinitions")
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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 org.springframework.cloud.gateway.actuate;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Marta Medio
|
||||
*/
|
||||
class GatewayEndpointInfo {
|
||||
|
||||
private String href;
|
||||
|
||||
private List<String> methods;
|
||||
|
||||
public String getHref() {
|
||||
return href;
|
||||
}
|
||||
|
||||
public void setHref(String href) {
|
||||
this.href = href;
|
||||
}
|
||||
|
||||
public String[] getMethods() {
|
||||
return methods.stream().toArray(String[]::new);
|
||||
}
|
||||
|
||||
GatewayEndpointInfo(String href, String method) {
|
||||
this.href = href;
|
||||
this.methods = Collections.singletonList(method);
|
||||
}
|
||||
|
||||
GatewayEndpointInfo(String href, List<String> methods) {
|
||||
this.href = href;
|
||||
this.methods = methods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
GatewayEndpointInfo that = (GatewayEndpointInfo) o;
|
||||
return Objects.equals(href, that.href) && Objects.equals(methods, that.methods);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(href, methods);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import java.util.Map;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
@@ -47,9 +48,9 @@ public class GatewayLegacyControllerEndpoint extends AbstractGatewayControllerEn
|
||||
public GatewayLegacyControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator,
|
||||
List<GlobalFilter> globalFilters, List<GatewayFilterFactory> gatewayFilterFactories,
|
||||
List<RoutePredicateFactory> routePredicates, RouteDefinitionWriter routeDefinitionWriter,
|
||||
RouteLocator routeLocator) {
|
||||
RouteLocator routeLocator, WebEndpointProperties webEndpointProperties) {
|
||||
super(routeDefinitionLocator, globalFilters, gatewayFilterFactories, routePredicates, routeDefinitionWriter,
|
||||
routeLocator);
|
||||
routeLocator, webEndpointProperties);
|
||||
}
|
||||
|
||||
@GetMapping("/routes")
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
@@ -817,9 +818,9 @@ public class GatewayAutoConfiguration {
|
||||
public GatewayControllerEndpoint gatewayControllerEndpoint(List<GlobalFilter> globalFilters,
|
||||
List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> routePredicates,
|
||||
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator,
|
||||
RouteDefinitionLocator routeDefinitionLocator) {
|
||||
RouteDefinitionLocator routeDefinitionLocator, WebEndpointProperties webEndpointProperties) {
|
||||
return new GatewayControllerEndpoint(globalFilters, gatewayFilters, routePredicates, routeDefinitionWriter,
|
||||
routeLocator, routeDefinitionLocator);
|
||||
routeLocator, routeDefinitionLocator, webEndpointProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -828,9 +829,10 @@ public class GatewayAutoConfiguration {
|
||||
public GatewayLegacyControllerEndpoint gatewayLegacyControllerEndpoint(
|
||||
RouteDefinitionLocator routeDefinitionLocator, List<GlobalFilter> globalFilters,
|
||||
List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> routePredicates,
|
||||
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
|
||||
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator,
|
||||
WebEndpointProperties webEndpointProperties) {
|
||||
return new GatewayLegacyControllerEndpoint(routeDefinitionLocator, globalFilters, gatewayFilters,
|
||||
routePredicates, routeDefinitionWriter, routeLocator);
|
||||
routePredicates, routeDefinitionWriter, routeLocator, webEndpointProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -68,6 +68,34 @@ public class GatewayControllerEndpointTests {
|
||||
@LocalServerPort
|
||||
int port;
|
||||
|
||||
@Test
|
||||
public void testEndpoints() {
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway").exchange()
|
||||
.expectStatus().isOk().expectBodyList(Map.class).consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).isNotEmpty();
|
||||
assertThat(responseBody).contains(
|
||||
Map.of("href", "/actuator/gateway/", "methods",
|
||||
List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/globalfilters", "methods",
|
||||
List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/refresh", "methods",
|
||||
List.of("POST")),
|
||||
Map.of("href", "/actuator/gateway/routedefinitions",
|
||||
"methods", List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/routefilters", "methods",
|
||||
List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/routepredicates", "methods",
|
||||
List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/routes", "methods",
|
||||
List.of("POST", "GET")),
|
||||
Map.of("href", "/actuator/gateway/routes/test-service",
|
||||
"methods", List.of("POST", "DELETE", "GET")),
|
||||
Map.of("href", "/actuator/gateway/routes/route_with_metadata",
|
||||
"methods", List.of("POST", "DELETE", "GET")));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefresh() {
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh").exchange().expectStatus()
|
||||
|
||||
Reference in New Issue
Block a user