Document asSubscriber stage (#28128)

* Unfortunately it seems the jdk9-only tests could not actually be compiled.
With these changes those can actually be compiled and ran again.

* Always link to jdk11 for java.* javadocs

* Update sbt-paradox-akka to fix linking to inner classes for javadoc
This commit is contained in:
Arnout Engelen 2019-12-05 16:40:05 +01:00 committed by GitHub
parent 619a4494d5
commit 25ad10f893
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 195 additions and 13 deletions

View file

@ -4,12 +4,51 @@ Integration with Reactive Streams, materializes into a `org.reactivestreams.Subs
@ref[Source operators](../index.md#source-operators)
@@@ div { .group-scala }
## Signature
@@signature [Source.scala](/akka-stream/src/main/scala/akka/stream/scaladsl/Source.scala) { #asSubscriber }
@@@ div { .group-scala }
@@snip[JavaFlowSupport.scala](/akka-stream/src/main/scala-jdk-9/akka/stream/scaladsl/JavaFlowSupport.scala) { #asSubscriber }
@@@
@@@ div { .group-java }
@@snip[JavaFlowSupport.java](/akka-stream/src/main/java-jdk-9/akka/stream/javadsl/JavaFlowSupport.java) { #asSubscriber }
@@@
## Description
TODO: We would welcome help on contributing descriptions and examples, see: https://github.com/akka/akka/issues/25646
If you want to create a @apidoc[Source] that gets its elements from another library that supports
[Reactive Streams](https://www.reactive-streams.org/), you can use `JavaFlowSupport.Source.asSubscriber`.
Each time this @apidoc[Source] is materialized, it produces a materialized value of type
@javadoc[java.util.concurrent.Flow.Subscriber](java.util.concurrent.Flow.Subscriber).
This @javadoc[Subscriber](java.util.concurrent.Flow.Subscriber) can be attached to a
[Reactive Streams](https://www.reactive-streams.org/) @javadoc[Publisher](java.util.concurrent.Flow.Publisher)
to populate it.
@@@ note
For JDK 8 users: since @javadoc[java.util.concurrent.Flow](java.util.concurrent.Flow) was introduced in JDK version 9,
if you are still on version 8 you may use the [org.reactivestreams](https://github.com/reactive-streams/reactive-streams-jvm#reactive-streams) library with `Source.asSubscriber` and `Flow.asSubscriber`.
@@@
## Example
Suppose we use a database client that supports [Reactive Streams](https://www.reactive-streams.org/),
we could create a @apidoc[Source] that queries the database for its rows. That @apidoc[Source] can then
be used for further processing, for example creating a @apidoc[Source] that contains the names of the
rows.
Note that since the database is queried for each materialization, the `rowSource` can be safely re-used.
Because both the database driver and Akka Streams support [Reactive Streams](https://www.reactive-streams.org/),
backpressure is applied throughout the stream, preventing us from running out of memory when the database
rows are consumed slower than they are produced by the database.
Scala
: @@snip [AsSubscriber.scala](/akka-docs/src/test/scala-jdk9-only/docs/stream/operators/source/AsSubscriber.scala) { #imports #example }
Java
: @@snip [AsSubscriber.java](/akka-docs/src/test/java-jdk9-only/jdocs/stream/operators/source/AsSubscriber.java) { #imports #example }

View file

@ -0,0 +1,67 @@
/*
* Copyright (C) 2019 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.stream.operators.source;
//#imports
import java.util.concurrent.Flow.Subscriber;
import java.util.concurrent.Flow.Publisher;
import akka.NotUsed;
import akka.stream.javadsl.Source;
import akka.stream.javadsl.JavaFlowSupport;
//#imports
import org.apache.commons.lang.NotImplementedException;
public interface AsSubscriber {
static class JavaFlowSupport {
public static final class Source {
public
// #api
static <T> akka.stream.javadsl.Source<T, Subscriber<T>> asSubscriber()
// #api
{
return akka.stream.javadsl.JavaFlowSupport.Source.<T>asSubscriber();
}
}
}
static class Row {
public String getField(String fieldName) {
throw new NotImplementedException();
}
}
static class DatabaseClient {
Publisher<Row> fetchRows() {
throw new NotImplementedException();
}
}
DatabaseClient databaseClient = null;
// #example
class Example {
Source<Row, NotUsed> rowSource =
JavaFlowSupport.Source.<Row>asSubscriber()
.mapMaterializedValue(
subscriber -> {
// For each materialization, fetch the rows from the database:
Publisher<Row> rows = databaseClient.fetchRows();
rows.subscribe(subscriber);
return NotUsed.getInstance();
});
public Source<String, NotUsed> names() {
// rowSource can be re-used, since it will start a new
// query for each materialization, fully supporting backpressure
// for each materialized stream:
return rowSource.map(row -> row.getField("name"));
}
}
// #example
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (C) 2019 Lightbend Inc. <https://www.lightbend.com>
*/
package docs.stream.operators.source;
//#imports
import java.util.concurrent.Flow.Subscriber;
import java.util.concurrent.Flow.Publisher;
import akka.NotUsed;
import akka.stream.scaladsl.Source;
import akka.stream.scaladsl.JavaFlowSupport;
//#imports
object AsSubscriber {
case class Row(name: String)
class DatabaseClient {
def fetchRows(): Publisher[Row] = ???
}
val databaseClient: DatabaseClient = ???;
// #example
val rowSource: Source[Row, NotUsed] =
JavaFlowSupport.Source.asSubscriber
.mapMaterializedValue(
(subscriber: Subscriber[Row]) => {
// For each materialization, fetch the rows from the database:
val rows: Publisher[Row] = databaseClient.fetchRows()
rows.subscribe(subscriber)
NotUsed
});
val names: Source[String, NotUsed] =
// rowSource can be re-used, since it will start a new
// query for each materialization, fully supporting backpressure
// for each materialized stream:
rowSource.map(row => row.name);
//#example
}

View file

@ -52,7 +52,9 @@ public final class JavaFlowSupport {
* See also {@code Source.asSubscriber} if wanting to integrate with {@link org.reactivestreams.Subscriber} instead
* (which carries the same semantics, however existed before RS's inclusion in Java 9).
*/
//#asSubscriber
public static <T> akka.stream.javadsl.Source<T, java.util.concurrent.Flow.Subscriber<T>> asSubscriber() {
//#asSubscriber
return akka.stream.javadsl.Source.<T>asSubscriber().mapMaterializedValue(JavaFlowAndRsConverters::asJava);
}
}

View file

@ -46,7 +46,9 @@ object JavaFlowSupport {
* @see See also [[Source.asSubscriber]] if wanting to integrate with [[org.reactivestreams.Subscriber]] instead
* (which carries the same semantics, however existed before RS's inclusion in Java 9).
*/
final def asSubscriber[T]: Source[T, juc.Flow.Subscriber[T]] =
//#asSubscriber
final def asSubscriber[T]: Source[T, java.util.concurrent.Flow.Subscriber[T]] =
//#asSubscriber
scaladsl.Source.asSubscriber[T].mapMaterializedValue(_.asJava)
}

View file

@ -102,7 +102,8 @@ lazy val akkaScalaNightly = akkaModule("akka-scala-nightly")
.disablePlugins(ValidatePullRequest, MimaPlugin, CopyrightHeaderInPr)
lazy val benchJmh = akkaModule("akka-bench-jmh")
.dependsOn(Seq(actor, actorTyped, stream, streamTests, persistence, distributedData, jackson, testkit).map(
.enablePlugins(Jdk9)
.dependsOn(Seq(actor, actorTyped, stream, streamTestkit, persistence, distributedData, jackson, testkit).map(
_ % "compile->compile;compile->test"): _*)
.settings(Dependencies.benchJmh)
.settings(javacOptions += "-parameters") // for Jackson
@ -170,6 +171,7 @@ lazy val distributedData = akkaModule("akka-distributed-data")
.enablePlugins(MultiNodeScalaTest)
lazy val docs = akkaModule("akka-docs")
.configs(akka.Jdk9.TestJdk9)
.dependsOn(
actor,
cluster,
@ -180,6 +182,7 @@ lazy val docs = akkaModule("akka-docs")
persistenceQuery,
distributedData,
stream,
stream % "TestJdk9->CompileJdk9",
actorTyped,
clusterTools % "compile->compile;test->test",
clusterSharding % "compile->compile;test->test",
@ -201,7 +204,8 @@ lazy val docs = akkaModule("akka-docs")
NoPublish,
ParadoxBrowse,
ScaladocNoVerificationOfDiagrams,
StreamOperatorsIndexGenerator)
StreamOperatorsIndexGenerator,
Jdk9)
.disablePlugins(MimaPlugin, WhiteSourcePlugin)
.disablePlugins(ScalafixPlugin)
@ -344,9 +348,14 @@ lazy val streamTestkit = akkaModule("akka-stream-testkit")
.disablePlugins(MimaPlugin)
lazy val streamTests = akkaModule("akka-stream-tests")
.dependsOn(streamTestkit % "test->test", remote % "test->test", stream)
.configs(akka.Jdk9.TestJdk9)
.dependsOn(
streamTestkit % "test->test",
remote % "test->test",
stream % "TestJdk9->CompileJdk9"
)
.settings(Dependencies.streamTests)
.enablePlugins(NoPublish)
.enablePlugins(NoPublish, Jdk9)
.disablePlugins(MimaPlugin, WhiteSourcePlugin)
lazy val streamTestsTck = akkaModule("akka-stream-tests-tck")

View file

@ -10,7 +10,9 @@ import sbt.Keys._
object Jdk9 extends AutoPlugin {
import JdkOptions.notOnJdk8
lazy val CompileJdk9 = config("CompileJdk9").extend(Compile)
val CompileJdk9 = config("CompileJdk9").extend(Compile)
val TestJdk9 = config("TestJdk9").extend(Test)
val SCALA_SOURCE_DIRECTORY = "scala-jdk-9"
val SCALA_TEST_SOURCE_DIRECTORY = "scala-jdk9-only"
@ -23,9 +25,23 @@ object Jdk9 extends AutoPlugin {
Seq(
(Compile / sourceDirectory).value / SCALA_SOURCE_DIRECTORY,
(Compile / sourceDirectory).value / JAVA_SOURCE_DIRECTORY)),
scalacOptions := AkkaBuild.DefaultScalacOptions ++ notOnJdk8(Seq("-release", "11")),
javacOptions := AkkaBuild.DefaultJavacOptions ++ notOnJdk8(Seq("--release", "11")))
val testJdk9Settings = Seq(
// following the scala-2.12, scala-sbt-1.0, ... convention
unmanagedSourceDirectories := notOnJdk8(
Seq(
(Test / sourceDirectory).value / SCALA_TEST_SOURCE_DIRECTORY,
(Test / sourceDirectory).value / JAVA_TEST_SOURCE_DIRECTORY)),
scalacOptions := AkkaBuild.DefaultScalacOptions ++ notOnJdk8(Seq("-release", "11")),
javacOptions := AkkaBuild.DefaultJavacOptions ++ notOnJdk8(Seq("--release", "11")),
classpathConfiguration := TestJdk9,
externalDependencyClasspath := (externalDependencyClasspath in Test).value
)
val compileSettings = Seq(
// It might have been more 'neat' to add the jdk9 products to the jar via packageBin/mappings, but that doesn't work with the OSGi plugin,
// so we add them to the fullClasspath instead.
@ -38,5 +54,7 @@ object Jdk9 extends AutoPlugin {
override lazy val projectSettings =
inConfig(CompileJdk9)(Defaults.compileSettings) ++
inConfig(CompileJdk9)(compileJdk9Settings) ++
compileSettings
compileSettings ++
inConfig(TestJdk9)(Defaults.testSettings) ++
inConfig(TestJdk9)(testJdk9Settings)
}

View file

@ -25,6 +25,8 @@ object Paradox {
"extref.ecs.base_url" -> "https://example.lightbend.com/v1/download/%s",
"scaladoc.akka.base_url" -> "https://doc.akka.io/api/akka/2.6",
"scaladoc.akka.http.base_url" -> "https://doc.akka.io/api/akka-http/current",
"javadoc.java.base_url" -> "https://docs.oracle.com/en/java/javase/11/docs/api/java.base/",
"javadoc.java.link_style" -> "direct",
"javadoc.akka.base_url" -> "https://doc.akka.io/japi/akka/2.6",
"javadoc.akka.link_style" -> "direct",
"javadoc.akka.http.base_url" -> "https://doc.akka.io/japi/akka-http/current",