1616
1717package org .springframework .cloud .gateway .actuate ;
1818
19+ import java .io .IOException ;
1920import java .net .URI ;
21+ import java .util .ArrayList ;
22+ import java .util .Arrays ;
2023import java .util .HashMap ;
2124import java .util .List ;
2225import java .util .Map ;
2326import java .util .Set ;
2427import java .util .stream .Collectors ;
28+ import java .util .stream .Stream ;
2529
2630import org .apache .commons .logging .Log ;
2731import org .apache .commons .logging .LogFactory ;
3438import org .springframework .cloud .gateway .filter .factory .GatewayFilterFactory ;
3539import org .springframework .cloud .gateway .handler .predicate .PredicateDefinition ;
3640import org .springframework .cloud .gateway .handler .predicate .RoutePredicateFactory ;
41+ import org .springframework .cloud .gateway .route .Route ;
3742import org .springframework .cloud .gateway .route .RouteDefinition ;
3843import org .springframework .cloud .gateway .route .RouteDefinitionLocator ;
3944import org .springframework .cloud .gateway .route .RouteDefinitionWriter ;
4247import org .springframework .context .ApplicationEventPublisher ;
4348import org .springframework .context .ApplicationEventPublisherAware ;
4449import org .springframework .core .Ordered ;
50+ import org .springframework .core .type .MethodMetadata ;
51+ import org .springframework .core .type .classreading .MetadataReader ;
52+ import org .springframework .core .type .classreading .SimpleMetadataReaderFactory ;
4553import org .springframework .http .HttpStatus ;
4654import org .springframework .http .ResponseEntity ;
4755import org .springframework .util .CollectionUtils ;
5159import org .springframework .web .bind .annotation .PathVariable ;
5260import org .springframework .web .bind .annotation .PostMapping ;
5361import org .springframework .web .bind .annotation .RequestBody ;
62+ import org .springframework .web .bind .annotation .RequestMapping ;
63+ import org .springframework .web .bind .annotation .RequestMethod ;
5464import org .springframework .web .bind .annotation .RequestParam ;
5565import org .springframework .web .server .ResponseStatusException ;
5666
@@ -61,6 +71,8 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
6171
6272 private static final Log log = LogFactory .getLog (GatewayControllerEndpoint .class );
6373
74+ private static final String ENDPOINT_PREFIX = "/actuator/gateway" ;
75+
6476 protected RouteDefinitionLocator routeDefinitionLocator ;
6577
6678 protected List <GlobalFilter > globalFilters ;
@@ -88,6 +100,53 @@ public AbstractGatewayControllerEndpoint(RouteDefinitionLocator routeDefinitionL
88100 this .routeLocator = routeLocator ;
89101 }
90102
103+ private List <GatewayEndpointInfo > getAvailableEndpointsForClass (String className ) {
104+ try {
105+ MetadataReader metadataReader = new SimpleMetadataReaderFactory ().getMetadataReader (className );
106+ Set <MethodMetadata > annotatedMethods = metadataReader .getAnnotationMetadata ()
107+ .getAnnotatedMethods (RequestMapping .class .getName ());
108+
109+ return annotatedMethods .stream ().map (method -> new GatewayEndpointInfo (ENDPOINT_PREFIX
110+ + ((String []) method .getAnnotationAttributes (RequestMapping .class .getName ()).get ("path" ))[0 ],
111+ ((RequestMethod []) method .getAnnotationAttributes (RequestMapping .class .getName ()).get ("method" ))[0 ]
112+ .name ()))
113+ .collect (Collectors .toList ());
114+ }
115+ catch (IOException exception ) {
116+ log .warn (exception .getMessage ());
117+ throw new ResponseStatusException (HttpStatus .INTERNAL_SERVER_ERROR , exception .getMessage ());
118+ }
119+ }
120+
121+ public static List <GatewayEndpointInfo > mergeEndpoints (List <GatewayEndpointInfo > listA ,
122+ List <GatewayEndpointInfo > listB ) {
123+ Map <String , List <String >> mergedMap = new HashMap <>();
124+
125+ Stream .concat (listA .stream (), listB .stream ()).forEach (e -> mergedMap
126+ .computeIfAbsent (e .getHref (), k -> new ArrayList <>()).addAll (Arrays .asList (e .getMethods ())));
127+
128+ return mergedMap .entrySet ().stream ().map (entry -> new GatewayEndpointInfo (entry .getKey (), entry .getValue ()))
129+ .collect (Collectors .toList ());
130+ }
131+
132+ GatewayEndpointInfo generateHref (Route r , GatewayEndpointInfo path ) {
133+ return new GatewayEndpointInfo (path .getHref ().replace ("{id}" , r .getId ()), Arrays .asList (path .getMethods ()));
134+ }
135+
136+ @ GetMapping ("/" )
137+ public Mono <List <GatewayEndpointInfo >> getEndpoints () {
138+ List <GatewayEndpointInfo > endpoints = mergeEndpoints (
139+ getAvailableEndpointsForClass (AbstractGatewayControllerEndpoint .class .getName ()),
140+ getAvailableEndpointsForClass (GatewayControllerEndpoint .class .getName ()));
141+
142+ return Flux .fromIterable (endpoints ).map (p -> p )
143+ .flatMap (path -> this .routeLocator .getRoutes ().map (r -> generateHref (r , path )).distinct ().collectList ()
144+ .flatMapMany (Flux ::fromIterable ))
145+ .distinct () // Ensure overall uniqueness
146+ .collectList ();
147+
148+ }
149+
91150 @ Override
92151 public void setApplicationEventPublisher (ApplicationEventPublisher publisher ) {
93152 this .publisher = publisher ;
0 commit comments