From 01bb526fa97084f78689d03b5645311838d1c964 Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Tue, 31 Jan 2017 20:49:07 -0700 Subject: [PATCH] Initial RouteWriter support --- .../gateway/actuate/GatewayEndpoint.java | 44 +++++++++++++++++-- .../cloud/gateway/api/RouteWriter.java | 13 ++++++ .../config/GatewayAutoConfiguration.java | 13 +++++- .../support/InMemoryRouteRepository.java | 41 +++++++++++++++++ .../gateway/test/GatewayTestApplication.java | 8 ++-- 5 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/springframework/cloud/gateway/api/RouteWriter.java create mode 100644 src/main/java/org/springframework/cloud/gateway/support/InMemoryRouteRepository.java diff --git a/src/main/java/org/springframework/cloud/gateway/actuate/GatewayEndpoint.java b/src/main/java/org/springframework/cloud/gateway/actuate/GatewayEndpoint.java index ac3f6d574..b0d948789 100644 --- a/src/main/java/org/springframework/cloud/gateway/actuate/GatewayEndpoint.java +++ b/src/main/java/org/springframework/cloud/gateway/actuate/GatewayEndpoint.java @@ -1,20 +1,27 @@ package org.springframework.cloud.gateway.actuate; +import java.net.URI; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import org.springframework.cloud.gateway.support.CachingRouteLocator; -import org.springframework.cloud.gateway.api.RouteLocator; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.cloud.gateway.api.Route; +import org.springframework.cloud.gateway.api.RouteLocator; +import org.springframework.cloud.gateway.api.RouteWriter; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.route.RouteFilter; import org.springframework.cloud.gateway.handler.FilteringWebHandler; +import org.springframework.cloud.gateway.support.CachingRouteLocator; import org.springframework.core.Ordered; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; 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.RestController; @@ -30,18 +37,23 @@ import reactor.core.publisher.Mono; @RequestMapping("/admin/gateway") public class GatewayEndpoint {/*extends AbstractEndpoint> {*/ + private static final Log log = LogFactory.getLog(GatewayEndpoint.class); + private RouteLocator routeLocator; private List globalFilters; private List routeFilters; private FilteringWebHandler filteringWebHandler; + private RouteWriter routeWriter; public GatewayEndpoint(RouteLocator routeLocator, List globalFilters, - List routeFilters, FilteringWebHandler filteringWebHandler) { + List routeFilters, FilteringWebHandler filteringWebHandler, + RouteWriter routeWriter) { //super("gateway"); this.routeLocator = routeLocator; this.globalFilters = globalFilters; this.routeFilters = routeFilters; this.filteringWebHandler = filteringWebHandler; + this.routeWriter = routeWriter; } /*@Override @@ -87,6 +99,32 @@ public class GatewayEndpoint {/*extends AbstractEndpoint> {* return this.routeLocator.getRoutes().collectList(); } +/* +http POST :8080/admin/gateway/routes/addreqhead2 \ +uri=http://httpbin.org/headers \ +predicates:='["Host=**.addrequestheader.org", "Url=/headers"]' \ +filters:='["AddRequestHeader=X-Request-Foo, Bar"]' +*/ + @PostMapping("/routes/{id}") + public Mono> save(@PathVariable String id, @RequestBody Mono route) { + return this.routeWriter.save(route.map(r -> { + r.setId(id); + log.debug("Saving route: " + route); + return r; + })).then(() -> { + GatewayEndpoint.this.refresh(); + return Mono.just(ResponseEntity.created(URI.create("/routes/"+id)).build()); + }); + } + + @DeleteMapping("/routes/{id}") + public Mono> delete(@PathVariable Mono id) { + return this.routeWriter.delete(id).then(() -> { + GatewayEndpoint.this.refresh(); + return Mono.just(ResponseEntity.ok().build()); + }); + } + @GetMapping("/routes/{id}") public Mono route(@PathVariable String id) { return this.routeLocator.getRoutes() diff --git a/src/main/java/org/springframework/cloud/gateway/api/RouteWriter.java b/src/main/java/org/springframework/cloud/gateway/api/RouteWriter.java new file mode 100644 index 000000000..d2a87c51c --- /dev/null +++ b/src/main/java/org/springframework/cloud/gateway/api/RouteWriter.java @@ -0,0 +1,13 @@ +package org.springframework.cloud.gateway.api; + +import reactor.core.publisher.Mono; + +/** + * @author Spencer Gibb + */ +public interface RouteWriter { + + Mono save(Mono route); + + Mono delete(Mono routeId); +} diff --git a/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index 41bd0b6c4..0b02fac20 100644 --- a/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -11,6 +11,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.gateway.actuate.GatewayEndpoint; import org.springframework.cloud.gateway.api.RouteLocator; +import org.springframework.cloud.gateway.api.RouteWriter; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter; import org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter; @@ -43,6 +44,7 @@ import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredic import org.springframework.cloud.gateway.handler.predicate.RoutePredicate; import org.springframework.cloud.gateway.handler.predicate.UrlRoutePredicate; import org.springframework.cloud.gateway.support.CachingRouteLocator; +import org.springframework.cloud.gateway.support.InMemoryRouteRepository; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -233,14 +235,21 @@ public class GatewayAutoConfiguration { return new SetStatusRouteFilter(); } + //TODO: control creation + @Bean + public InMemoryRouteRepository inMemoryRouteRepository() { + return new InMemoryRouteRepository(); + } + @Configuration @ConditionalOnClass(Endpoint.class) protected static class GatewayActuatorConfiguration { @Bean public GatewayEndpoint gatewayEndpoint(RouteLocator routeLocator, List globalFilters, - List routeFilters, FilteringWebHandler filteringWebHandler) { - return new GatewayEndpoint(routeLocator, globalFilters, routeFilters, filteringWebHandler); + List routeFilters, FilteringWebHandler filteringWebHandler, + RouteWriter routeWriter) { + return new GatewayEndpoint(routeLocator, globalFilters, routeFilters, filteringWebHandler, routeWriter); } } diff --git a/src/main/java/org/springframework/cloud/gateway/support/InMemoryRouteRepository.java b/src/main/java/org/springframework/cloud/gateway/support/InMemoryRouteRepository.java new file mode 100644 index 000000000..7638b05db --- /dev/null +++ b/src/main/java/org/springframework/cloud/gateway/support/InMemoryRouteRepository.java @@ -0,0 +1,41 @@ +package org.springframework.cloud.gateway.support; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.cloud.gateway.api.Route; +import org.springframework.cloud.gateway.api.RouteLocator; +import org.springframework.cloud.gateway.api.RouteWriter; + +import static java.util.Collections.synchronizedMap; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * @author Spencer Gibb + */ +public class InMemoryRouteRepository implements RouteLocator, RouteWriter { + + private final Map routes = synchronizedMap(new LinkedHashMap()); + + @Override + public Mono save(Mono route) { + return route.doOnNext(r -> routes.put(r.getId(), r)).then(); + //route.subscribe(r -> ); + //return Mono.empty(); + } + + @Override + public Mono delete(Mono routeId) { + return routeId.then(id -> { + routes.remove(id); + return Mono.empty(); + }); + } + + @Override + public Flux getRoutes() { + return Flux.fromIterable(routes.values()); + } +} diff --git a/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java b/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java index 552da4f6a..988612b7c 100644 --- a/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java +++ b/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java @@ -11,6 +11,7 @@ import org.springframework.cloud.gateway.api.RouteLocator; import org.springframework.cloud.gateway.config.GatewayProperties; import org.springframework.cloud.gateway.config.PropertiesRouteLocator; import org.springframework.cloud.gateway.discovery.DiscoveryClientRouteLocator; +import org.springframework.cloud.gateway.support.InMemoryRouteRepository; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @@ -44,9 +45,10 @@ public class GatewayTestApplication { @Bean @Primary - public RouteLocator compositeRouteLocator(DiscoveryClientRouteLocator discoveryClientRouteLocator, - PropertiesRouteLocator propertiesRouteLocator) { - final Flux flux = Flux.just(discoveryClientRouteLocator, propertiesRouteLocator); + public RouteLocator compositeRouteLocator(InMemoryRouteRepository inMemoryRouteRepository, + DiscoveryClientRouteLocator discoveryClientRouteLocator, + PropertiesRouteLocator propertiesRouteLocator) { + final Flux flux = Flux.just(inMemoryRouteRepository, discoveryClientRouteLocator, propertiesRouteLocator); final CompositeRouteLocator composite = new CompositeRouteLocator(flux); return new CachingRouteLocator(composite); }