Repository navigation
Conversation
Motivation: Java sees Flow, Source, Sink and the stream shapes as invariant, so a handler typed Flow<Message, TextMessage, NotUsed> (what Flow.of(Message.class).map(m -> TextMessage.create(...)) infers) or a Source<TextMessage, ?> could not be passed to the Java WebSocket APIs. Modification: Widen the handler parameters to Flow<? super Message, ? extends Message, T>, and to Graph<? extends FlowShape<? super Message, ? extends Message>, ?> / Graph<? extends SinkShape<? super Message>, ?> / Graph<? extends SourceShape<? extends Message>, ?>, in WebSocketDirectives.handleWebSocketMessages*, Http.singleWebSocketRequest, WebSocket.handleWebSocketRequestWith, WebSocketUpgrade.handleMessagesWith (and the scaladsl overrides) and testkit WSTestRequestBuilding.WS. Add MiMa excludes for the generic-signature-only changes. Result: Java callers can pass WebSocket handlers with Message subtype outputs and supertype inputs. The erased signatures are unchanged. Tests: - sbt "http-tests/Test/testOnly ...WebSocketHandlerTypesTest" - passed on Scala 2.13 and 3.3.8 - sbt "http-tests/Test/testOnly ...WebSocketDirectivesSpec" "http-core/Test/testOnly ...WebSocketServerSpec ...WebSocketIntegrationSpec" - passed - sbt http-core/mimaReportBinaryIssues http/mimaReportBinaryIssues - passed References: Refs apache#1378
Motivation: With the Java WebSocket APIs accepting Message subtypes, the Arrays.<Message>asList(...) witnesses and (Message) casts in the docs example and WSEchoTestClientApp are no longer needed. Modification: Use List.of(TextMessage...) sources and Flow<Message, TextMessage, ...> client flows directly in WebSocketClientExampleTest (the half-closed-WebSocket-finite snippet) and WSEchoTestClientApp. Result: The docs show the natural way to write a Java WebSocket client flow. Tests: - sbt docs/Test/javafmt http-core/Test/javafmt - no changes needed - compile not run locally (machine overloaded); left to CI References: Refs apache#1378
16 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Java sees
Flow,Source,Sinkand the stream shapes as invariant. So a WebSocket handler typedFlow<Message, TextMessage, NotUsed>, which is whatFlow.of(Message.class).map(m -> TextMessage.create(...))infers, cannot be passed to the Java WebSocket APIs. Neither can aSource<TextMessage, ?>as the outbound side. Our own docs and tests work around this withArrays.<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):WebSocketDirectives.handleWebSocketMessages*(6 overloads)Flow<Message, Message, T>Flow<? super Message, ? extends Message, T>Http.singleWebSocketRequest(3 overloads)Flow<Message, Message, T>WebSocket.handleWebSocketRequestWithFlow<Message, Message, ?>WSTestRequestBuilding.WS(2 overloads)Flow<Message, Message, T>WebSocketUpgrade.handleMessagesWith(9 overloads), plus the scaladslWebSocketUpgradeoverridesGraph<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
Graphparameters need the outer? extendstoo. Without it, Java would not accept aFlow<Message, TextMessage, ...>as aGraph<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,SinkShapeandSourceShapeare 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
IncompatibleSignatureProblemfor the changed methods (12 inhttp-core, 3 inhttp; 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 newjava-websocket-handler-wildcards.excludesfiles.http-testkithas MiMa disabled. A third-party class implementing the javadslWebSocketUpgradedirectly would need to update its overrides. That should be rare, since the interface exists for the request attribute.The workarounds in the Java docs example and test app are removed: the
half-closed-WebSocket-finitesnippet inWebSocketClientExampleTestandWSEchoTestClientAppnow useList.of(TextMessage...)sources andFlow<Message, TextMessage, ...>client flows directly, withoutArrays.<Message>asList(...)witnesses or(Message)casts. The rendered docs pick up the simpler snippet.Result
Java callers can pass WebSocket handlers whose outputs are
Messagesubtypes and whose inputs areMessagesupertypes, 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 aFlow<Message, TextMessage, NotUsed>handler throughout:serverSideHandlersAcceptMessageSubtypes: everyhandleWebSocketMessages*overload (plain,shouldCompress,ForProtocol,ForOptionalProtocol),WebSocketUpgrade.handleMessagesWith(flow)andWebSocket.handleWebSocketRequestWith, each checked viaWSProbe(send "hi", expect "HI", completion)upgradeSinkAndSourceAcceptMessageSubtypeAndSupertype:handleMessagesWith(Sink.ignore(), Source<TextMessage, NotUsed>).Sink.ignore()is aSink<Object, ...>, which covers the? super Messageinput sidetestkitClientHandlerAcceptsMessageSubtype: testkitWS(...)with aFlow<Message, TextMessage, NotUsed>client handlersingleWebSocketRequestAcceptsMessageSubtype:Http.singleWebSocketRequestwith aFlow<Message, TextMessage, CompletionStage<Message>>against a real bound serversbt "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 excludessbt docs/Test/compile: passed before the docs cleanup commit; that commit was only formatted locally (javafmt, no changes) and its compile is left to CIhttp-core/http/http-testkit,http-tests/Test/javafmtCheck(JDK 17) and header checks: passedReferences
Refs #1378