From 665d7df1e6eb6f9f340c27a0b4f9baa7fad2a16d Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Fri, 29 Oct 2021 15:00:37 -0400 Subject: [PATCH] Adds Visitor pattern to visit each node of Predicate. (#2423) This allows users to inspect the configuration of predicates and perform work on them. --- .../cloud/gateway/handler/AsyncPredicate.java | 28 +++++++- .../predicate/AfterRoutePredicateFactory.java | 5 ++ .../BeforeRoutePredicateFactory.java | 5 ++ .../BetweenRoutePredicateFactory.java | 5 ++ .../CookieRoutePredicateFactory.java | 5 ++ .../handler/predicate/GatewayPredicate.java | 33 ++++++++- .../HeaderRoutePredicateFactory.java | 5 ++ .../predicate/HostRoutePredicateFactory.java | 5 ++ .../predicate/PathRoutePredicateFactory.java | 5 ++ .../predicate/QueryRoutePredicateFactory.java | 5 ++ .../ReadBodyRoutePredicateFactory.java | 5 ++ .../RemoteAddrRoutePredicateFactory.java | 5 ++ .../WeightRoutePredicateFactory.java | 5 ++ .../cloud/gateway/support/HasConfig.java | 25 +++++++ .../cloud/gateway/support/Visitor.java | 30 ++++++++ .../GatewayPredicateVisitorTests.java | 70 +++++++++++++++++++ 16 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/HasConfig.java create mode 100644 spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/Visitor.java create mode 100644 spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/GatewayPredicateVisitorTests.java diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/AsyncPredicate.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/AsyncPredicate.java index 73e43408..e15694eb 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/AsyncPredicate.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/AsyncPredicate.java @@ -23,13 +23,15 @@ import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate; +import org.springframework.cloud.gateway.support.HasConfig; +import org.springframework.cloud.gateway.support.Visitor; import org.springframework.util.Assert; import org.springframework.web.server.ServerWebExchange; /** * @author Ben Hale */ -public interface AsyncPredicate extends Function> { +public interface AsyncPredicate extends Function>, HasConfig { default AsyncPredicate and(AsyncPredicate other) { return new AndAsyncPredicate<>(this, other); @@ -47,6 +49,10 @@ public interface AsyncPredicate extends Function> { return new OrAsyncPredicate<>(this, other); } + default void accept(Visitor visitor) { + visitor.visit(this); + } + static AsyncPredicate from(Predicate predicate) { return new DefaultAsyncPredicate<>(GatewayPredicate.wrapIfNeeded(predicate)); } @@ -69,6 +75,14 @@ public interface AsyncPredicate extends Function> { return this.delegate.toString(); } + @Override + public void accept(Visitor visitor) { + if (delegate instanceof GatewayPredicate) { + GatewayPredicate gatewayPredicate = (GatewayPredicate) delegate; + gatewayPredicate.accept(visitor); + } + } + } class NegateAsyncPredicate implements AsyncPredicate { @@ -110,6 +124,12 @@ public interface AsyncPredicate extends Function> { return Mono.from(left.apply(t)).flatMap(result -> !result ? Mono.just(false) : Mono.from(right.apply(t))); } + @Override + public void accept(Visitor visitor) { + left.accept(visitor); + right.accept(visitor); + } + @Override public String toString() { return String.format("(%s && %s)", this.left, this.right); @@ -135,6 +155,12 @@ public interface AsyncPredicate extends Function> { return Mono.from(left.apply(t)).flatMap(result -> result ? Mono.just(true) : Mono.from(right.apply(t))); } + @Override + public void accept(Visitor visitor) { + left.accept(visitor); + right.accept(visitor); + } + @Override public String toString() { return String.format("(%s || %s)", this.left, this.right); diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/AfterRoutePredicateFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/AfterRoutePredicateFactory.java index 3256de86..e8cd073e 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/AfterRoutePredicateFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/AfterRoutePredicateFactory.java @@ -53,6 +53,11 @@ public class AfterRoutePredicateFactory extends AbstractRoutePredicateFactory { +public interface GatewayPredicate extends Predicate, HasConfig { @Override default Predicate and(Predicate other) { @@ -38,6 +40,10 @@ public interface GatewayPredicate extends Predicate { return new OrGatewayPredicate(this, wrapIfNeeded(other)); } + default void accept(Visitor visitor) { + visitor.visit(this); + } + static GatewayPredicate wrapIfNeeded(Predicate other) { GatewayPredicate right; @@ -64,6 +70,14 @@ public interface GatewayPredicate extends Predicate { return this.delegate.test(exchange); } + @Override + public void accept(Visitor visitor) { + if (delegate instanceof GatewayPredicate) { + GatewayPredicate gatewayPredicate = (GatewayPredicate) delegate; + gatewayPredicate.accept(visitor); + } + } + @Override public String toString() { return this.delegate.getClass().getSimpleName(); @@ -85,6 +99,11 @@ public interface GatewayPredicate extends Predicate { return !this.predicate.test(t); } + @Override + public void accept(Visitor visitor) { + predicate.accept(visitor); + } + @Override public String toString() { return String.format("!%s", this.predicate); @@ -110,6 +129,12 @@ public interface GatewayPredicate extends Predicate { return (this.left.test(t) && this.right.test(t)); } + @Override + public void accept(Visitor visitor) { + left.accept(visitor); + right.accept(visitor); + } + @Override public String toString() { return String.format("(%s && %s)", this.left, this.right); @@ -135,6 +160,12 @@ public interface GatewayPredicate extends Predicate { return (this.left.test(t) || this.right.test(t)); } + @Override + public void accept(Visitor visitor) { + left.accept(visitor); + right.accept(visitor); + } + @Override public String toString() { return String.format("(%s || %s)", this.left, this.right); diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/HeaderRoutePredicateFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/HeaderRoutePredicateFactory.java index 19e32813..3be06201 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/HeaderRoutePredicateFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/HeaderRoutePredicateFactory.java @@ -79,6 +79,11 @@ public class HeaderRoutePredicateFactory extends AbstractRoutePredicateFactory predicate = AsyncPredicate.from(pathRoutePredicateFactory.apply(pathRoutePredicateFactory.newConfig())) + .and(AsyncPredicate.from(hostRoutePredicateFactory.apply(hostRoutePredicateFactory.newConfig()))) + .and(readBodyRoutePredicateFactory1.applyAsync(readBodyRoutePredicateFactory1.newConfig())) + .and(readBodyRoutePredicateFactory2.applyAsync(readBodyRoutePredicateFactory2.newConfig())); + + Route route = Route.async().id("git").uri("http://myuri").asyncPredicate(predicate).build(); + ArrayList configs = new ArrayList<>(); + route.getPredicate().accept(p -> configs.add(p.getConfig())); + + assertThat(configs).hasSize(4).hasExactlyElementsOfTypes(PathRoutePredicateFactory.Config.class, + HostRoutePredicateFactory.Config.class, ReadBodyRoutePredicateFactory.Config.class, + ReadBodyRoutePredicateFactory.Config.class); + } + + @Test + public void predicateVisitVisitsEachNode() { + PathRoutePredicateFactory pathRoutePredicateFactory = new PathRoutePredicateFactory(); + HostRoutePredicateFactory hostRoutePredicateFactory = new HostRoutePredicateFactory(); + Predicate predicate = pathRoutePredicateFactory.apply(pathRoutePredicateFactory.newConfig()) + .and(hostRoutePredicateFactory.apply(hostRoutePredicateFactory.newConfig())); + + Route route = Route.async().id("git").uri("http://myuri").predicate(predicate).build(); + ArrayList configs = new ArrayList<>(); + route.getPredicate().accept(p -> configs.add(p.getConfig())); + + assertThat(configs).hasSize(2).hasExactlyElementsOfTypes(PathRoutePredicateFactory.Config.class, + HostRoutePredicateFactory.Config.class); + } + +}