spring-cloud-gateway
spring-cloud-gateway copied to clipboard
Identical Routes are not considered equal: AsyncPredicate does not override equals()
Two logically equal Route
s are not programmatically equal since Route
's equals()
compares predicates, and the AsyncPredicate
class does not override equals()
(hence you get a simple reference comparison). Here's a test that reproduces the issue:
class RouteEqualityTest {
@Test
void testTwoIdenticalRouteAreEqual() {
assertThat(getRouteBuilderStub().build()).isEqualTo(getRouteBuilderStub().build());
}
private Route.AsyncBuilder getRouteBuilderStub() {
return Route.async()
.id("123")
.uri("https://example.com")
.predicate(exchange -> exchange.getRequest()
.getPath()
.value()
.equals("/test-path"));
}
}
Does this cause problems for you?
It does in tests. I found a workaround by creating my own PathOnlyAsyncPredicate
that calls equals()
between path strings, but it's, well, a workaround. It doesn't feel right that Route
has equals()
that is in effect no different from ==
It boils down to the fact that we can't meaningfully compare lambdas since we can't compare method implementations. That is, we can compare state, not behavior. We can:
- Make
AsyncPredicate
implementations extend someAbstractAsyncPredicate
instead of implementingAsyncPredicate
directly.AbstractAsyncPredicate
would have have an overriddenequals()
that delegates to its delegatePredicate
, for exampleGatewayPredicate
(this.delegate.equals(that.delegate)
) - Introduce stateful OOB
GatewayPredicate
s and encourage their use (and introduce state in already existing implementations). For example,PathRoutePredicateFactory.apply(Config)
could return somePathGatewayPredicate
instead of an anonymous subclass.PathGatewayPredicate
would have a constructor accepting a collection of path patterns andequals()
that compares the contents of those collections - Explicitly state in the documentation to
Route
'sequals()
that it only meaningfully compares twoRoute
instances if statefulPredicate
s are injected, otherwise it in effect compares references (generally, Spring Cloud Gateway has substantially underdelivered on javadocs)
@spencergibb are you on board with the plan? If so, I can put forward a PR
Also, I'd like to know where I can ask some set-up questions. I read the README, looked for a community forum, to no avail. Specifically, I'm curious where org.springframework.cloud:spring-cloud-build:pom:4.1.1-SNAPSHOT
is supposed to be pulled from (Maven build fails). Should I manually add it as a jar? It's not in Maven Central (not that version). It's unlikely you expect contributors to define custom repositories in pom
s
https://repo.spring.io/snapshot is the snapshot repo. See https://start.spring.io/#!type=maven-project&language=java&platformVersion=3.3.0-SNAPSHOT&packaging=jar&jvmVersion=17&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.demo for a maven example.
For 1) there are already implementations of AsyncPredicate
that implement toString()
for example, adding equals()
there makes sense.
For 2) GatewayPredicate
implements HasConfig
which is the state. I wonder if a default equals()
in GatewayPredicate
that calls getConfig()
.
WDYT?
Thank you for your assistance! I appreciate your help. Before we move on (and before I address your points concerning this particular issue), I'd like to ask you: are there good reasons why these repositories (they indeed contain necessary artifacts, I defined them in the pom
, and Maven build succeeded)
- aren't already in the
pom
; - are not mentioned in the
README
orCONTRIBUTING
?
If there aren't any, I suggest we do at least one of the above to make onboarding of new contributors easier and, as a result, make the backlog of issues related to this project shrink even faster
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
</pluginRepositories>