2016-01-13 16:25:24 +01:00
|
|
|
/**
|
2016-02-23 12:58:39 +01:00
|
|
|
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
|
2016-01-13 16:25:24 +01:00
|
|
|
*/
|
|
|
|
|
package docs.stream;
|
|
|
|
|
|
|
|
|
|
import static org.junit.Assert.*;
|
|
|
|
|
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.List;
|
2016-01-21 16:37:26 +01:00
|
|
|
import java.util.concurrent.CompletionStage;
|
2016-01-13 16:25:24 +01:00
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
2016-01-20 10:00:37 +02:00
|
|
|
import akka.NotUsed;
|
2016-01-13 16:25:24 +01:00
|
|
|
import akka.stream.ClosedShape;
|
|
|
|
|
import akka.stream.SourceShape;
|
2016-02-11 16:39:25 +01:00
|
|
|
import docs.AbstractJavaTest;
|
2016-01-13 16:25:24 +01:00
|
|
|
import org.junit.AfterClass;
|
|
|
|
|
import org.junit.BeforeClass;
|
|
|
|
|
import org.junit.Test;
|
|
|
|
|
|
|
|
|
|
import scala.concurrent.Await;
|
|
|
|
|
import scala.concurrent.Future;
|
|
|
|
|
import scala.concurrent.duration.Duration;
|
|
|
|
|
import akka.actor.ActorSystem;
|
|
|
|
|
import akka.japi.Pair;
|
|
|
|
|
import akka.stream.*;
|
|
|
|
|
import akka.stream.javadsl.*;
|
|
|
|
|
import akka.testkit.JavaTestKit;
|
|
|
|
|
|
2016-04-12 20:24:08 +08:00
|
|
|
public class GraphDSLDocTest extends AbstractJavaTest {
|
2016-01-13 16:25:24 +01:00
|
|
|
|
|
|
|
|
static ActorSystem system;
|
2016-02-11 16:39:25 +01:00
|
|
|
static Materializer mat;
|
2016-01-13 16:25:24 +01:00
|
|
|
|
|
|
|
|
@BeforeClass
|
|
|
|
|
public static void setup() {
|
2016-04-12 20:24:08 +08:00
|
|
|
system = ActorSystem.create("GraphDSLDocTest");
|
2016-02-11 16:39:25 +01:00
|
|
|
mat = ActorMaterializer.create(system);
|
2016-01-13 16:25:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@AfterClass
|
|
|
|
|
public static void tearDown() {
|
|
|
|
|
JavaTestKit.shutdownActorSystem(system);
|
|
|
|
|
system = null;
|
2016-02-11 16:39:25 +01:00
|
|
|
mat = null;
|
2016-01-13 16:25:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void demonstrateBuildSimpleGraph() throws Exception {
|
2016-04-12 20:24:08 +08:00
|
|
|
//#simple-graph-dsl
|
2016-01-20 10:00:37 +02:00
|
|
|
final Source<Integer, NotUsed> in = Source.from(Arrays.asList(1, 2, 3, 4, 5));
|
2016-01-21 16:37:26 +01:00
|
|
|
final Sink<List<String>, CompletionStage<List<String>>> sink = Sink.head();
|
2016-01-20 10:00:37 +02:00
|
|
|
final Flow<Integer, Integer, NotUsed> f1 = Flow.of(Integer.class).map(elem -> elem + 10);
|
|
|
|
|
final Flow<Integer, Integer, NotUsed> f2 = Flow.of(Integer.class).map(elem -> elem + 20);
|
|
|
|
|
final Flow<Integer, String, NotUsed> f3 = Flow.of(Integer.class).map(elem -> elem.toString());
|
|
|
|
|
final Flow<Integer, Integer, NotUsed> f4 = Flow.of(Integer.class).map(elem -> elem + 30);
|
2016-01-13 16:25:24 +01:00
|
|
|
|
2016-01-21 16:37:26 +01:00
|
|
|
final RunnableGraph<CompletionStage<List<String>>> result =
|
2016-07-08 11:15:52 +02:00
|
|
|
RunnableGraph.fromGraph(
|
|
|
|
|
GraphDSL // create() function binds sink, out which is sink's out port and builder DSL
|
|
|
|
|
.create( // we need to reference out's shape in the builder DSL below (in to() function)
|
|
|
|
|
sink, // previously created sink (Sink)
|
|
|
|
|
(builder, out) -> { // variables: builder (GraphDSL.Builder) and out (SinkShape)
|
2016-01-13 16:25:24 +01:00
|
|
|
final UniformFanOutShape<Integer, Integer> bcast = builder.add(Broadcast.create(2));
|
|
|
|
|
final UniformFanInShape<Integer, Integer> merge = builder.add(Merge.create(2));
|
|
|
|
|
|
|
|
|
|
final Outlet<Integer> source = builder.add(in).out();
|
|
|
|
|
builder.from(source).via(builder.add(f1))
|
|
|
|
|
.viaFanOut(bcast).via(builder.add(f2)).viaFanIn(merge)
|
2016-07-08 11:15:52 +02:00
|
|
|
.via(builder.add(f3.grouped(1000))).to(out); // to() expects a SinkShape
|
2016-01-13 16:25:24 +01:00
|
|
|
builder.from(bcast).via(builder.add(f4)).toFanIn(merge);
|
|
|
|
|
return ClosedShape.getInstance();
|
|
|
|
|
}));
|
2016-04-12 20:24:08 +08:00
|
|
|
//#simple-graph-dsl
|
2016-01-21 16:37:26 +01:00
|
|
|
final List<String> list = result.run(mat).toCompletableFuture().get(3, TimeUnit.SECONDS);
|
2016-01-13 16:25:24 +01:00
|
|
|
final String[] res = list.toArray(new String[] {});
|
|
|
|
|
Arrays.sort(res, null);
|
|
|
|
|
assertArrayEquals(new String[] { "31", "32", "33", "34", "35", "41", "42", "43", "44", "45" }, res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@SuppressWarnings("unused")
|
|
|
|
|
public void demonstrateConnectErrors() {
|
|
|
|
|
try {
|
|
|
|
|
//#simple-graph
|
2016-01-20 10:00:37 +02:00
|
|
|
final RunnableGraph<NotUsed> g =
|
|
|
|
|
RunnableGraph.<NotUsed>fromGraph(
|
2016-01-13 16:25:24 +01:00
|
|
|
GraphDSL
|
|
|
|
|
.create((b) -> {
|
|
|
|
|
final SourceShape<Integer> source1 = b.add(Source.from(Arrays.asList(1, 2, 3, 4, 5)));
|
|
|
|
|
final SourceShape<Integer> source2 = b.add(Source.from(Arrays.asList(1, 2, 3, 4, 5)));
|
|
|
|
|
final FanInShape2<Integer, Integer, Pair<Integer, Integer>> zip = b.add(Zip.create());
|
|
|
|
|
b.from(source1).toInlet(zip.in0());
|
|
|
|
|
b.from(source2).toInlet(zip.in1());
|
|
|
|
|
return ClosedShape.getInstance();
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
// unconnected zip.out (!) => "The inlets [] and outlets [] must correspond to the inlets [] and outlets [ZipWith2.out]"
|
|
|
|
|
//#simple-graph
|
2016-08-03 14:06:57 +02:00
|
|
|
org.junit.Assert.fail("expected IllegalArgumentException");
|
2016-01-13 16:25:24 +01:00
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
|
assertTrue(e != null && e.getMessage() != null && e.getMessage().contains("must correspond to"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void demonstrateReusingFlowInGraph() throws Exception {
|
2016-04-12 20:24:08 +08:00
|
|
|
//#graph-dsl-reusing-a-flow
|
2016-01-21 16:37:26 +01:00
|
|
|
final Sink<Integer, CompletionStage<Integer>> topHeadSink = Sink.head();
|
|
|
|
|
final Sink<Integer, CompletionStage<Integer>> bottomHeadSink = Sink.head();
|
2016-01-20 10:00:37 +02:00
|
|
|
final Flow<Integer, Integer, NotUsed> sharedDoubler = Flow.of(Integer.class).map(elem -> elem * 2);
|
2016-01-13 16:25:24 +01:00
|
|
|
|
2016-01-21 16:37:26 +01:00
|
|
|
final RunnableGraph<Pair<CompletionStage<Integer>, CompletionStage<Integer>>> g =
|
|
|
|
|
RunnableGraph.<Pair<CompletionStage<Integer>, CompletionStage<Integer>>>fromGraph(
|
2016-01-13 16:25:24 +01:00
|
|
|
GraphDSL.create(
|
|
|
|
|
topHeadSink, // import this sink into the graph
|
|
|
|
|
bottomHeadSink, // and this as well
|
|
|
|
|
Keep.both(),
|
|
|
|
|
(b, top, bottom) -> {
|
|
|
|
|
final UniformFanOutShape<Integer, Integer> bcast =
|
|
|
|
|
b.add(Broadcast.create(2));
|
|
|
|
|
|
|
|
|
|
b.from(b.add(Source.single(1))).viaFanOut(bcast)
|
|
|
|
|
.via(b.add(sharedDoubler)).to(top);
|
|
|
|
|
b.from(bcast).via(b.add(sharedDoubler)).to(bottom);
|
|
|
|
|
return ClosedShape.getInstance();
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
2016-04-12 20:24:08 +08:00
|
|
|
//#graph-dsl-reusing-a-flow
|
2016-01-21 16:37:26 +01:00
|
|
|
final Pair<CompletionStage<Integer>, CompletionStage<Integer>> pair = g.run(mat);
|
|
|
|
|
assertEquals(Integer.valueOf(2), pair.first().toCompletableFuture().get(3, TimeUnit.SECONDS));
|
|
|
|
|
assertEquals(Integer.valueOf(2), pair.second().toCompletableFuture().get(3, TimeUnit.SECONDS));
|
2016-01-13 16:25:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void demonstrateMatValue() throws Exception {
|
2016-04-12 20:24:08 +08:00
|
|
|
//#graph-dsl-matvalue
|
2016-01-21 16:37:26 +01:00
|
|
|
final Sink<Integer, CompletionStage<Integer>> foldSink = Sink.<Integer, Integer> fold(0, (a, b) -> {
|
2016-01-13 16:25:24 +01:00
|
|
|
return a + b;
|
|
|
|
|
});
|
|
|
|
|
|
2016-01-21 16:37:26 +01:00
|
|
|
final Flow<CompletionStage<Integer>, Integer, NotUsed> flatten =
|
|
|
|
|
Flow.<CompletionStage<Integer>>create().mapAsync(4, x -> x);
|
2016-01-13 16:25:24 +01:00
|
|
|
|
2016-01-21 16:37:26 +01:00
|
|
|
final Flow<Integer, Integer, CompletionStage<Integer>> foldingFlow = Flow.fromGraph(
|
2016-01-13 16:25:24 +01:00
|
|
|
GraphDSL.create(foldSink,
|
|
|
|
|
(b, fold) -> {
|
|
|
|
|
return FlowShape.of(
|
|
|
|
|
fold.in(),
|
|
|
|
|
b.from(b.materializedValue()).via(b.add(flatten)).out());
|
|
|
|
|
}));
|
2016-04-12 20:24:08 +08:00
|
|
|
//#graph-dsl-matvalue
|
2016-01-13 16:25:24 +01:00
|
|
|
|
2016-04-12 20:24:08 +08:00
|
|
|
//#graph-dsl-matvalue-cycle
|
2016-01-13 16:25:24 +01:00
|
|
|
// This cannot produce any value:
|
2016-01-21 16:37:26 +01:00
|
|
|
final Source<Integer, CompletionStage<Integer>> cyclicSource = Source.fromGraph(
|
2016-01-13 16:25:24 +01:00
|
|
|
GraphDSL.create(foldSink,
|
|
|
|
|
(b, fold) -> {
|
|
|
|
|
// - Fold cannot complete until its upstream mapAsync completes
|
|
|
|
|
// - mapAsync cannot complete until the materialized Future produced by
|
|
|
|
|
// fold completes
|
|
|
|
|
// As a result this Source will never emit anything, and its materialited
|
|
|
|
|
// Future will never complete
|
|
|
|
|
b.from(b.materializedValue()).via(b.add(flatten)).to(fold);
|
|
|
|
|
return SourceShape.of(b.from(b.materializedValue()).via(b.add(flatten)).out());
|
|
|
|
|
}));
|
|
|
|
|
|
2016-04-12 20:24:08 +08:00
|
|
|
//#graph-dsl-matvalue-cycle
|
2016-01-13 16:25:24 +01:00
|
|
|
}
|
|
|
|
|
}
|