Documentation improvements

* Re enabling Java tests in akka-docs (they were not run before)
* Fixed bug #19764
* #19735 Rewrote every sample using the deprecated PushPullStage and friends
  using GraphStage
* Pruned old unused graph images
* Added missing graffle file for new graph images
This commit is contained in:
Johan Andrén 2016-02-11 16:39:25 +01:00
parent 8f3c5aa17f
commit 737991c01c
103 changed files with 1136 additions and 4749 deletions

View file

@ -5,15 +5,11 @@ package docs.stream.javadsl.cookbook;
import akka.NotUsed;
import akka.actor.ActorSystem;
import akka.stream.ActorMaterializer;
import akka.stream.Materializer;
import akka.stream.*;
import akka.stream.javadsl.Flow;
import akka.stream.javadsl.Sink;
import akka.stream.javadsl.Source;
import akka.stream.stage.Context;
import akka.stream.stage.PushPullStage;
import akka.stream.stage.PushStage;
import akka.stream.stage.SyncDirective;
import akka.stream.stage.*;
import akka.testkit.JavaTestKit;
import akka.util.ByteString;
import org.junit.AfterClass;
@ -24,6 +20,7 @@ import scala.Tuple2;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
@ -31,19 +28,21 @@ import static org.junit.Assert.assertTrue;
public class RecipeByteStrings extends RecipeTest {
static ActorSystem system;
static Materializer mat;
@BeforeClass
public static void setup() {
system = ActorSystem.create("RecipeByteStrings");
mat = ActorMaterializer.create(system);
}
@AfterClass
public static void tearDown() {
JavaTestKit.shutdownActorSystem(system);
system = null;
mat = null;
}
final Materializer mat = ActorMaterializer.create(system);
final Source<ByteString, NotUsed> rawBytes = Source.from(Arrays.asList(
ByteString.fromArray(new byte[] { 1, 2 }),
@ -57,42 +56,77 @@ public class RecipeByteStrings extends RecipeTest {
final int CHUNK_LIMIT = 2;
//#bytestring-chunker
class Chunker extends PushPullStage<ByteString, ByteString> {
class Chunker extends GraphStage<FlowShape<ByteString, ByteString>> {
private final int chunkSize;
private ByteString buffer = ByteString.empty();
public Inlet<ByteString> in = Inlet.<ByteString>create("Chunker.in");
public Outlet<ByteString> out = Outlet.<ByteString>create("Chunker.out");
private FlowShape<ByteString, ByteString> shape = FlowShape.of(in, out);
public Chunker(int chunkSize) {
this.chunkSize = chunkSize;
}
@Override
public SyncDirective onPush(ByteString elem, Context<ByteString> ctx) {
buffer = buffer.concat(elem);
return emitChunkOrPull(ctx);
public FlowShape<ByteString, ByteString> shape() {
return shape;
}
@Override
public SyncDirective onPull(Context<ByteString> ctx) {
return emitChunkOrPull(ctx);
public GraphStageLogic createLogic(Attributes inheritedAttributes) {
return new GraphStageLogic(shape) {
private ByteString buffer = ByteString.empty();
{
setHandler(out, new AbstractOutHandler(){
@Override
public void onPull() throws Exception {
if (isClosed(in)) emitChunk();
else pull(in);
}
});
setHandler(in, new AbstractInHandler() {
@Override
public void onPush() throws Exception {
ByteString elem = grab(in);
buffer = buffer.concat(elem);
emitChunk();
}
@Override
public void onUpstreamFinish() throws Exception {
if (buffer.isEmpty()) completeStage();
// elements left in buffer, keep accepting downstream pulls
// and push from buffer until buffer is emitted
}
});
}
private void emitChunk() {
if (buffer.isEmpty()) {
if (isClosed(in)) completeStage();
else pull(in);
} else {
Tuple2<ByteString, ByteString> split = buffer.splitAt(chunkSize);
ByteString chunk = split._1();
buffer = split._2();
push(out, chunk);
}
}
};
}
public SyncDirective emitChunkOrPull(Context<ByteString> ctx) {
if (buffer.isEmpty()) {
return ctx.pull();
} else {
Tuple2<ByteString, ByteString> split = buffer.splitAt(chunkSize);
ByteString emit = split._1();
buffer = split._2();
return ctx.push(emit);
}
}
}
//#bytestring-chunker
{
//#bytestring-chunker2
Source<ByteString, NotUsed> chunksStream =
rawBytes.transform(() -> new Chunker(CHUNK_LIMIT));
rawBytes.via(new Chunker(CHUNK_LIMIT));
//#bytestring-chunker2
CompletionStage<List<ByteString>> chunksFuture = chunksStream.limit(10).runWith(Sink.seq(), mat);
@ -119,22 +153,49 @@ public class RecipeByteStrings extends RecipeTest {
final int SIZE_LIMIT = 9;
//#bytes-limiter
class ByteLimiter extends PushStage<ByteString, ByteString> {
class ByteLimiter extends GraphStage<FlowShape<ByteString, ByteString>> {
final long maximumBytes;
private int count = 0;
public Inlet<ByteString> in = Inlet.<ByteString>create("ByteLimiter.in");
public Outlet<ByteString> out = Outlet.<ByteString>create("ByteLimiter.out");
private FlowShape<ByteString, ByteString> shape = FlowShape.of(in, out);
public ByteLimiter(long maximumBytes) {
this.maximumBytes = maximumBytes;
}
@Override
public SyncDirective onPush(ByteString chunk, Context<ByteString> ctx) {
count += chunk.size();
if (count > maximumBytes) {
return ctx.fail(new IllegalStateException("Too much bytes"));
} else {
return ctx.push(chunk);
}
public FlowShape<ByteString, ByteString> shape() {
return shape;
}
@Override
public GraphStageLogic createLogic(Attributes inheritedAttributes) {
return new GraphStageLogic(shape) {
private int count = 0;
{
setHandler(out, new AbstractOutHandler() {
@Override
public void onPull() throws Exception {
pull(in);
}
});
setHandler(in, new AbstractInHandler() {
@Override
public void onPush() throws Exception {
ByteString chunk = grab(in);
count += chunk.size();
if (count > maximumBytes) {
failStage(new IllegalStateException("Too much bytes"));
} else {
push(out, chunk);
}
}
});
}
};
}
}
//#bytes-limiter
@ -142,7 +203,7 @@ public class RecipeByteStrings extends RecipeTest {
{
//#bytes-limiter2
Flow<ByteString, ByteString, NotUsed> limiter =
Flow.of(ByteString.class).transform(() -> new ByteLimiter(SIZE_LIMIT));
Flow.of(ByteString.class).via(new ByteLimiter(SIZE_LIMIT));
//#bytes-limiter2
final Source<ByteString, NotUsed> bytes1 = Source.from(Arrays.asList(
@ -167,11 +228,12 @@ public class RecipeByteStrings extends RecipeTest {
boolean thrown = false;
try {
bytes2.via(limiter).limit(10).runWith(Sink.seq(), mat).toCompletableFuture().get(3, TimeUnit.SECONDS);
} catch (IllegalStateException ex) {
} catch (ExecutionException ex) {
assertEquals(ex.getCause().getClass(), IllegalStateException.class);
thrown = true;
}
assertTrue("Expected IllegalStateException to be thrown", thrown);
}
};
}
@ -187,7 +249,7 @@ public class RecipeByteStrings extends RecipeTest {
ByteString.fromArray(new byte[] { 7, 8, 9 })));
//#compacting-bytestrings
Source<ByteString, NotUsed> compacted = rawBytes.map(bs -> bs.compact());
Source<ByteString, NotUsed> compacted = rawBytes.map(ByteString::compact);
//#compacting-bytestrings
List<ByteString> got = compacted.limit(10).runWith(Sink.seq(), mat).toCompletableFuture().get(3, TimeUnit.SECONDS);