pjfanning opened a new pull request, #1382:
URL: https://github.com/apache/pekko-http/pull/1382
### Motivation
Java sees `Flow`, `Source`, `Sink` and the stream shapes as invariant. So a
WebSocket handler typed `Flow<Message, TextMessage, NotUsed>`, which is what
`Flow.of(Message.class).map(m -> TextMessage.create(...))` infers, cannot be
passed to the Java WebSocket APIs. Neither can a `Source<TextMessage, ?>` as
the outbound side. Our own docs and tests work around this with
`Arrays.<Message>asList(...)` and `(Message) TextMessage.create(...)` casts.
This is suggested PR 4 of #1378.
### Modification
Widen the handler parameters, using wildcards on both sides (outputs `?
extends Message`, inputs `? super Message`):
| API | Before | After |
|---|---|---|
| `WebSocketDirectives.handleWebSocketMessages*` (6 overloads) |
`Flow<Message, Message, T>` | `Flow<? super Message, ? extends Message, T>` |
| `Http.singleWebSocketRequest` (3 overloads) | `Flow<Message, Message, T>`
| same as above |
| `WebSocket.handleWebSocketRequestWith` | `Flow<Message, Message, ?>` |
same as above |
| testkit `WSTestRequestBuilding.WS` (2 overloads) | `Flow<Message, Message,
T>` | same as above |
| `WebSocketUpgrade.handleMessagesWith` (9 overloads), plus the scaladsl
`WebSocketUpgrade` overrides | `Graph<FlowShape<Message, Message>, ?>`,
`Graph<SinkShape<Message>, ?>`, `Graph<SourceShape<Message>, ?>` | `Graph<?
extends FlowShape<? super Message, ? extends Message>, ?>`, `Graph<? extends
SinkShape<? super Message>, ?>`, `Graph<? extends SourceShape<? extends
Message>, ?>` |
The `Graph` parameters need the outer `? extends` too. Without it, Java
would not accept a `Flow<Message, TextMessage, ...>` as a `Graph<FlowShape<?
super Message, ? extends Message>, ...>`.
Inside the Scala implementations, the wildcard arguments are cast to the
previous exact types before being handed to scaladsl. This is safe because
`Graph`, `FlowShape`, `SinkShape` and `SourceShape` are declared variant. Scala
2.13 accepts the widening as a plain type ascription, but Scala 3 does not
infer that subtyping for the nested wildcards, hence the casts.
The `Predicate<Message>` (`shouldCompress`) parameters are unchanged,
because Java lambdas already adapt to them.
MiMa reports `IncompatibleSignatureProblem` for the changed methods (12 in
`http-core`, 3 in `http`; the other overloads are new in 2.0.0). The erased JVM
signatures are unchanged, so existing compiled callers still link and existing
Java sources still compile. The excludes are in new
`java-websocket-handler-wildcards.excludes` files. `http-testkit` has MiMa
disabled. A third-party class implementing the javadsl `WebSocketUpgrade`
directly would need to update its overrides. That should be rare, since the
interface exists for the request attribute.
No docs change in this PR: the docs examples keep compiling unchanged.
Removing their `(Message)` casts and `Arrays.<Message>asList` witnesses can be
a follow-up.
### Result
Java callers can pass WebSocket handlers whose outputs are `Message`
subtypes and whose inputs are `Message` supertypes, on both the server and the
client side.
### Tests
- `sbt "http-tests/Test/testOnly
org.apache.pekko.http.javadsl.server.directives.WebSocketHandlerTypesTest"`:
passed on Scala 2.13 and 3.3.8. The new Java test uses a `Flow<Message,
TextMessage, NotUsed>` handler throughout:
- `serverSideHandlersAcceptMessageSubtypes`: every
`handleWebSocketMessages*` overload (plain, `shouldCompress`, `ForProtocol`,
`ForOptionalProtocol`), `WebSocketUpgrade.handleMessagesWith(flow)` and
`WebSocket.handleWebSocketRequestWith`, each checked via `WSProbe` (send "hi",
expect "HI", completion)
- `upgradeSinkAndSourceAcceptMessageSubtypeAndSupertype`:
`handleMessagesWith(Sink.ignore(), Source<TextMessage, NotUsed>)`.
`Sink.ignore()` is a `Sink<Object, ...>`, which covers the `? super Message`
input side
- `testkitClientHandlerAcceptsMessageSubtype`: testkit `WS(...)` with a
`Flow<Message, TextMessage, NotUsed>` client handler
- `singleWebSocketRequestAcceptsMessageSubtype`:
`Http.singleWebSocketRequest` with a `Flow<Message, TextMessage,
CompletionStage<Message>>` against a real bound server
- all of these fail to compile against the old signatures
- Existing specs still pass: `sbt "http-tests/Test/testOnly
org.apache.pekko.http.scaladsl.server.directives.WebSocketDirectivesSpec"
"http-core/Test/testOnly
org.apache.pekko.http.impl.engine.ws.WebSocketServerSpec
org.apache.pekko.http.impl.engine.ws.WebSocketIntegrationSpec"`
- `sbt http-core/mimaReportBinaryIssues http/mimaReportBinaryIssues`: passed
with the new excludes
- `sbt docs/Test/compile`: passed
- scalafmt checks for `http-core` / `http` / `http-testkit`,
`http-tests/Test/javafmtCheck` (JDK 17) and header checks: passed
### References
Refs #1378
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]