2018-10-29 17:19:37 +08:00
|
|
|
/*
|
2019-01-02 18:55:26 +08:00
|
|
|
* Copyright (C) 2015-2019 Lightbend Inc. <https://www.lightbend.com>
|
2016-01-13 16:25:24 +01:00
|
|
|
*/
|
2018-03-13 23:45:55 +09:00
|
|
|
|
2017-03-16 09:30:00 +01:00
|
|
|
package jdocs.stream;
|
2016-01-13 16:25:24 +01:00
|
|
|
|
|
|
|
|
import static org.junit.Assert.assertEquals;
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.List;
|
2016-01-21 16:37:26 +01:00
|
|
|
import java.util.concurrent.CompletionStage;
|
2016-02-11 16:39:25 +01:00
|
|
|
import java.util.concurrent.ExecutionException;
|
2016-01-13 16:25:24 +01:00
|
|
|
import java.util.concurrent.TimeUnit;
|
2016-01-20 10:00:37 +02:00
|
|
|
|
|
|
|
|
import akka.NotUsed;
|
2017-07-26 16:23:46 +02:00
|
|
|
import akka.japi.pf.PFBuilder;
|
2017-03-16 09:30:00 +01:00
|
|
|
import jdocs.AbstractJavaTest;
|
2017-03-17 03:02:47 +08:00
|
|
|
import akka.testkit.javadsl.TestKit;
|
2016-01-13 16:25:24 +01:00
|
|
|
import org.junit.AfterClass;
|
|
|
|
|
import org.junit.BeforeClass;
|
|
|
|
|
import org.junit.Test;
|
|
|
|
|
|
|
|
|
|
import akka.actor.ActorSystem;
|
|
|
|
|
import akka.stream.Materializer;
|
|
|
|
|
import akka.stream.Supervision;
|
|
|
|
|
import akka.stream.javadsl.Flow;
|
|
|
|
|
import akka.stream.ActorAttributes;
|
|
|
|
|
import akka.stream.javadsl.Sink;
|
|
|
|
|
import akka.stream.javadsl.Source;
|
|
|
|
|
import akka.japi.function.Function;
|
|
|
|
|
|
2016-02-11 16:39:25 +01:00
|
|
|
public class FlowErrorDocTest extends AbstractJavaTest {
|
2016-01-13 16:25:24 +01:00
|
|
|
|
|
|
|
|
private static ActorSystem system;
|
|
|
|
|
|
|
|
|
|
@BeforeClass
|
|
|
|
|
public static void setup() {
|
2019-01-12 04:00:53 +08:00
|
|
|
system = ActorSystem.create("FlowDocTest");
|
2016-01-13 16:25:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@AfterClass
|
|
|
|
|
public static void tearDown() {
|
2019-01-12 04:00:53 +08:00
|
|
|
TestKit.shutdownActorSystem(system);
|
|
|
|
|
system = null;
|
2016-01-13 16:25:24 +01:00
|
|
|
}
|
2019-01-12 04:00:53 +08:00
|
|
|
|
2016-02-11 16:39:25 +01:00
|
|
|
@Test(expected = ExecutionException.class)
|
2016-01-13 16:25:24 +01:00
|
|
|
public void demonstrateFailStream() throws Exception {
|
2019-01-12 04:00:53 +08:00
|
|
|
// #stop
|
|
|
|
|
final Source<Integer, NotUsed> source =
|
|
|
|
|
Source.from(Arrays.asList(0, 1, 2, 3, 4, 5)).map(elem -> 100 / elem);
|
2016-01-21 16:37:26 +01:00
|
|
|
final Sink<Integer, CompletionStage<Integer>> fold =
|
2019-01-12 04:00:53 +08:00
|
|
|
Sink.<Integer, Integer>fold(0, (acc, elem) -> acc + elem);
|
2019-08-23 18:19:27 +02:00
|
|
|
final CompletionStage<Integer> result = source.runWith(fold, system);
|
2016-01-13 16:25:24 +01:00
|
|
|
// division by zero will fail the stream and the
|
2019-08-23 18:19:27 +02:00
|
|
|
// result here will be a CompletionStage failed with ArithmeticException
|
2019-01-12 04:00:53 +08:00
|
|
|
// #stop
|
2016-01-13 16:25:24 +01:00
|
|
|
|
2016-01-21 16:37:26 +01:00
|
|
|
result.toCompletableFuture().get(3, TimeUnit.SECONDS);
|
2016-01-13 16:25:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void demonstrateResumeStream() throws Exception {
|
2019-01-12 04:00:53 +08:00
|
|
|
// #resume
|
|
|
|
|
final Function<Throwable, Supervision.Directive> decider =
|
|
|
|
|
exc -> {
|
|
|
|
|
if (exc instanceof ArithmeticException) return Supervision.resume();
|
|
|
|
|
else return Supervision.stop();
|
|
|
|
|
};
|
|
|
|
|
final Source<Integer, NotUsed> source =
|
2019-08-23 18:19:27 +02:00
|
|
|
Source.from(Arrays.asList(0, 1, 2, 3, 4, 5))
|
|
|
|
|
.map(elem -> 100 / elem)
|
|
|
|
|
.withAttributes(ActorAttributes.withSupervisionStrategy(decider));
|
2019-01-12 04:00:53 +08:00
|
|
|
final Sink<Integer, CompletionStage<Integer>> fold = Sink.fold(0, (acc, elem) -> acc + elem);
|
2019-08-23 18:19:27 +02:00
|
|
|
final CompletionStage<Integer> result = source.runWith(fold, system);
|
2016-01-13 16:25:24 +01:00
|
|
|
// the element causing division by zero will be dropped
|
2019-08-23 18:19:27 +02:00
|
|
|
// result here will be a CompletionStage completed with 228
|
2019-01-12 04:00:53 +08:00
|
|
|
// #resume
|
2016-01-13 16:25:24 +01:00
|
|
|
|
2016-01-21 16:37:26 +01:00
|
|
|
assertEquals(Integer.valueOf(228), result.toCompletableFuture().get(3, TimeUnit.SECONDS));
|
2016-01-13 16:25:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void demonstrateResumeSectionStream() throws Exception {
|
2019-01-12 04:00:53 +08:00
|
|
|
// #resume-section
|
|
|
|
|
final Function<Throwable, Supervision.Directive> decider =
|
|
|
|
|
exc -> {
|
|
|
|
|
if (exc instanceof ArithmeticException) return Supervision.resume();
|
|
|
|
|
else return Supervision.stop();
|
|
|
|
|
};
|
2016-01-20 10:00:37 +02:00
|
|
|
final Flow<Integer, Integer, NotUsed> flow =
|
2019-01-12 04:00:53 +08:00
|
|
|
Flow.of(Integer.class)
|
|
|
|
|
.filter(elem -> 100 / elem < 50)
|
|
|
|
|
.map(elem -> 100 / (5 - elem))
|
|
|
|
|
.withAttributes(ActorAttributes.withSupervisionStrategy(decider));
|
|
|
|
|
final Source<Integer, NotUsed> source = Source.from(Arrays.asList(0, 1, 2, 3, 4, 5)).via(flow);
|
2016-01-21 16:37:26 +01:00
|
|
|
final Sink<Integer, CompletionStage<Integer>> fold =
|
2019-01-12 04:00:53 +08:00
|
|
|
Sink.<Integer, Integer>fold(0, (acc, elem) -> acc + elem);
|
2019-08-23 18:19:27 +02:00
|
|
|
final CompletionStage<Integer> result = source.runWith(fold, system);
|
2016-01-13 16:25:24 +01:00
|
|
|
// the elements causing division by zero will be dropped
|
2019-08-23 18:19:27 +02:00
|
|
|
// result here will be a Future completed with 150
|
2019-01-12 04:00:53 +08:00
|
|
|
// #resume-section
|
2016-01-13 16:25:24 +01:00
|
|
|
|
2016-01-21 16:37:26 +01:00
|
|
|
assertEquals(Integer.valueOf(150), result.toCompletableFuture().get(3, TimeUnit.SECONDS));
|
2016-01-13 16:25:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void demonstrateRestartSectionStream() throws Exception {
|
2019-01-12 04:00:53 +08:00
|
|
|
// #restart-section
|
|
|
|
|
final Function<Throwable, Supervision.Directive> decider =
|
|
|
|
|
exc -> {
|
|
|
|
|
if (exc instanceof IllegalArgumentException) return Supervision.restart();
|
|
|
|
|
else return Supervision.stop();
|
|
|
|
|
};
|
2016-01-20 10:00:37 +02:00
|
|
|
final Flow<Integer, Integer, NotUsed> flow =
|
2019-01-12 04:00:53 +08:00
|
|
|
Flow.of(Integer.class)
|
|
|
|
|
.scan(
|
|
|
|
|
0,
|
|
|
|
|
(acc, elem) -> {
|
|
|
|
|
if (elem < 0) throw new IllegalArgumentException("negative not allowed");
|
|
|
|
|
else return acc + elem;
|
|
|
|
|
})
|
|
|
|
|
.withAttributes(ActorAttributes.withSupervisionStrategy(decider));
|
|
|
|
|
final Source<Integer, NotUsed> source = Source.from(Arrays.asList(1, 3, -1, 5, 7)).via(flow);
|
|
|
|
|
final CompletionStage<List<Integer>> result =
|
2019-08-23 18:19:27 +02:00
|
|
|
source.grouped(1000).runWith(Sink.<List<Integer>>head(), system);
|
2016-01-13 16:25:24 +01:00
|
|
|
// the negative element cause the scan stage to be restarted,
|
|
|
|
|
// i.e. start from 0 again
|
2019-08-23 18:19:27 +02:00
|
|
|
// result here will be a Future completed with List(0, 1, 4, 0, 5, 12)
|
2019-01-12 04:00:53 +08:00
|
|
|
// #restart-section
|
|
|
|
|
|
2016-01-13 16:25:24 +01:00
|
|
|
assertEquals(
|
2019-01-12 04:00:53 +08:00
|
|
|
Arrays.asList(0, 1, 4, 0, 5, 12), result.toCompletableFuture().get(3, TimeUnit.SECONDS));
|
2016-01-13 16:25:24 +01:00
|
|
|
}
|
|
|
|
|
|
2017-07-26 16:23:46 +02:00
|
|
|
@Test
|
|
|
|
|
public void demonstrateRecover() {
|
2019-01-12 04:00:53 +08:00
|
|
|
// #recover
|
|
|
|
|
Source.from(Arrays.asList(0, 1, 2, 3, 4, 5, 6))
|
|
|
|
|
.map(
|
|
|
|
|
n -> {
|
|
|
|
|
if (n < 5) return n.toString();
|
|
|
|
|
else throw new RuntimeException("Boom!");
|
|
|
|
|
})
|
|
|
|
|
.recover(new PFBuilder().match(RuntimeException.class, ex -> "stream truncated").build())
|
2019-08-23 18:19:27 +02:00
|
|
|
.runForeach(System.out::println, system);
|
2019-01-12 04:00:53 +08:00
|
|
|
// #recover
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Output:
|
|
|
|
|
//#recover-output
|
|
|
|
|
0
|
|
|
|
|
1
|
|
|
|
|
2
|
|
|
|
|
3
|
|
|
|
|
4
|
|
|
|
|
stream truncated
|
|
|
|
|
//#recover-output
|
|
|
|
|
*/
|
2017-07-26 16:23:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
2019-01-12 04:00:53 +08:00
|
|
|
public void demonstrateRecoverWithRetries() {
|
|
|
|
|
// #recoverWithRetries
|
2017-07-26 16:23:46 +02:00
|
|
|
Source<String, NotUsed> planB = Source.from(Arrays.asList("five", "six", "seven", "eight"));
|
|
|
|
|
|
2019-01-12 04:00:53 +08:00
|
|
|
Source.from(Arrays.asList(0, 1, 2, 3, 4, 5, 6))
|
|
|
|
|
.map(
|
|
|
|
|
n -> {
|
|
|
|
|
if (n < 5) return n.toString();
|
|
|
|
|
else throw new RuntimeException("Boom!");
|
|
|
|
|
})
|
|
|
|
|
.recoverWithRetries(
|
|
|
|
|
1, // max attempts
|
|
|
|
|
new PFBuilder().match(RuntimeException.class, ex -> planB).build())
|
2019-08-23 18:19:27 +02:00
|
|
|
.runForeach(System.out::println, system);
|
2019-01-12 04:00:53 +08:00
|
|
|
// #recoverWithRetries
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Output:
|
|
|
|
|
//#recoverWithRetries-output
|
|
|
|
|
0
|
|
|
|
|
1
|
|
|
|
|
2
|
|
|
|
|
3
|
|
|
|
|
4
|
|
|
|
|
five
|
|
|
|
|
six
|
|
|
|
|
seven
|
|
|
|
|
eight
|
|
|
|
|
//#recoverWithRetries-output
|
|
|
|
|
*/
|
2017-07-26 16:23:46 +02:00
|
|
|
}
|
2016-01-13 16:25:24 +01:00
|
|
|
}
|