Skip to content
This repository was archived by the owner on Nov 1, 2024. It is now read-only.

Commit 61f52db

Browse files
committed
Added support for turning builders directly to Akka streams
1 parent 8f1e99f commit 61f52db

File tree

3 files changed

+108
-49
lines changed

3 files changed

+108
-49
lines changed

akka/src/main/java/com/lightbend/microprofile/reactive/streams/akka/AkkaEngine.java

Lines changed: 106 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
import akka.stream.Attributes;
1616
import akka.stream.Materializer;
1717
import akka.stream.javadsl.*;
18+
import org.eclipse.microprofile.reactive.streams.CompletionBuilder;
19+
import org.eclipse.microprofile.reactive.streams.ProcessorBuilder;
20+
import org.eclipse.microprofile.reactive.streams.PublisherBuilder;
21+
import org.eclipse.microprofile.reactive.streams.SubscriberBuilder;
1822
import org.eclipse.microprofile.reactive.streams.SubscriberWithResult;
1923
import org.eclipse.microprofile.reactive.streams.spi.Graph;
2024
import org.eclipse.microprofile.reactive.streams.spi.ReactiveStreamsEngine;
@@ -45,7 +49,8 @@ public AkkaEngine(Materializer materializer) {
4549

4650
@Override
4751
public <T> Publisher<T> buildPublisher(Graph graph) throws UnsupportedStageException {
48-
// Optimization - if it's just a publisher, return it directly
52+
// Optimization - if it's just a pub
53+
// lisher, return it directly
4954
Stage firstStage = graph.getStages().iterator().next();
5055
if (graph.getStages().size() == 1 && firstStage instanceof Stage.PublisherStage) {
5156
return (Publisher) ((Stage.PublisherStage) firstStage).getRsPublisher();
@@ -55,13 +60,21 @@ public <T> Publisher<T> buildPublisher(Graph graph) throws UnsupportedStageExcep
5560
.toMat(Sink.asPublisher(AsPublisher.WITHOUT_FANOUT), Keep.right()));
5661
}
5762

63+
/**
64+
* Convert a publisher builder to a source.
65+
*/
66+
public <T> Source<T, NotUsed> buildSource(PublisherBuilder<T> publisher) throws UnsupportedStageException {
67+
return buildSource(publisher.toGraph());
68+
}
69+
5870
private <T> Source<T, NotUsed> buildSource(Graph graph) throws UnsupportedStageException {
5971
Source source = null;
6072
Flow flow = Flow.create();
6173
for (Stage stage : graph.getStages()) {
6274
if (source == null) {
6375
source = toSource(stage);
64-
} else {
76+
}
77+
else {
6578
flow = applyStage(flow, stage);
6679
}
6780
}
@@ -71,15 +84,26 @@ private <T> Source<T, NotUsed> buildSource(Graph graph) throws UnsupportedStageE
7184

7285
@Override
7386
public <T, R> SubscriberWithResult<T, R> buildSubscriber(Graph graph) throws UnsupportedStageException {
87+
return (SubscriberWithResult) materialize(Source.asSubscriber()
88+
.toMat(buildSink(graph), (subscriber, result) ->
89+
new SubscriberWithResult((Subscriber) subscriber, (CompletionStage) result)));
90+
}
91+
92+
/**
93+
* Convert a subscriber builder to a sink.
94+
*/
95+
public <T, R> Sink<T, CompletionStage<R>> buildSink(SubscriberBuilder<T, R> subscriber) throws UnsupportedStageException {
96+
return buildSink(subscriber.toGraph());
97+
}
98+
99+
private <T, R> Sink<T, CompletionStage<R>> buildSink(Graph graph) throws UnsupportedStageException {
74100
Flow flow = Flow.create();
75101
for (Stage stage : graph.getStages()) {
76102
if (stage.hasOutlet()) {
77103
flow = applyStage(flow, stage);
78-
} else {
79-
return (SubscriberWithResult) materialize(Source.asSubscriber()
80-
.via(flow)
81-
.toMat(toSink(stage), (subscriber, result) ->
82-
new SubscriberWithResult((Subscriber) subscriber, (CompletionStage) result)));
104+
}
105+
else {
106+
return flow.toMat(toSink(stage), Keep.right());
83107
}
84108
}
85109

@@ -96,24 +120,48 @@ public <T, R> Processor<T, R> buildProcessor(Graph graph) throws UnsupportedStag
96120
}
97121
}
98122

123+
return (Processor) materialize(buildFlow(graph).toProcessor());
124+
}
125+
126+
/**
127+
* Convert a processor builder to a flow.
128+
*/
129+
public <T, R> Flow<T, R, NotUsed> buildFlow(ProcessorBuilder<T, R> processor) throws UnsupportedStageException {
130+
return buildFlow(processor.toGraph());
131+
}
132+
133+
private <T, R> Flow<T, R, NotUsed> buildFlow(Graph graph) throws UnsupportedStageException {
99134
Flow flow = Flow.create();
100135
for (Stage stage : graph.getStages()) {
101136
flow = applyStage(flow, stage);
102137
}
103-
return (Processor) materialize(flow.toProcessor());
138+
return flow;
104139
}
105140

106141
@Override
107142
public <T> CompletionStage<T> buildCompletion(Graph graph) throws UnsupportedStageException {
143+
return materialize(buildRunnableGraph(graph));
144+
}
145+
146+
/**
147+
* Convert a completion builder to a runnable graph.
148+
*/
149+
public <T> RunnableGraph<CompletionStage<T>> buildCompletion(CompletionBuilder<T> completion) throws UnsupportedStageException {
150+
return buildRunnableGraph(completion.toGraph());
151+
}
152+
153+
private <T> RunnableGraph<CompletionStage<T>> buildRunnableGraph(Graph graph) throws UnsupportedStageException {
108154
Source source = null;
109155
Flow flow = Flow.create();
110156
for (Stage stage : graph.getStages()) {
111157
if (source == null) {
112158
source = toSource(stage);
113-
} else if (stage.hasOutlet()) {
159+
}
160+
else if (stage.hasOutlet()) {
114161
flow = applyStage(flow, stage);
115-
} else {
116-
return (CompletionStage) materialize(source.via(flow).toMat(toSink(stage), Keep.right()));
162+
}
163+
else {
164+
return source.via(flow).toMat(toSink(stage), Keep.right());
117165
}
118166
}
119167

@@ -124,43 +172,53 @@ private Flow applyStage(Flow flow, Stage stage) {
124172
if (stage instanceof Stage.Map) {
125173
Function<Object, Object> mapper = (Function) ((Stage.Map) stage).getMapper();
126174
return flow.map(mapper::apply);
127-
} else if (stage instanceof Stage.Filter) {
175+
}
176+
else if (stage instanceof Stage.Filter) {
128177
Predicate<Object> predicate = (Predicate) (((Stage.Filter) stage).getPredicate()).get();
129178
return flow.filter(predicate::test);
130-
} else if (stage instanceof Stage.FlatMap) {
179+
}
180+
else if (stage instanceof Stage.FlatMap) {
131181
Function<Object, Graph> mapper = (Function) ((Stage.FlatMap) stage).getMapper();
132182
return flow.flatMapConcat(e -> buildSource(mapper.apply(e)));
133-
} else if (stage instanceof Stage.TakeWhile) {
183+
}
184+
else if (stage instanceof Stage.TakeWhile) {
134185
Predicate<Object> predicate = (Predicate) (((Stage.TakeWhile) stage).getPredicate()).get();
135186
boolean inclusive = ((Stage.TakeWhile) stage).isInclusive();
136187
return flow.takeWhile(predicate::test, inclusive);
137-
} else if (stage instanceof Stage.FlatMapCompletionStage) {
188+
}
189+
else if (stage instanceof Stage.FlatMapCompletionStage) {
138190
Function<Object, CompletionStage<Object>> mapper = (Function) ((Stage.FlatMapCompletionStage) stage).getMapper();
139191
return flow.mapAsync(1, mapper::apply);
140-
} else if (stage instanceof Stage.FlatMapIterable) {
192+
}
193+
else if (stage instanceof Stage.FlatMapIterable) {
141194
Function<Object, Iterable<Object>> mapper = (Function) ((Stage.FlatMapIterable) stage).getMapper();
142195
return flow.mapConcat(mapper::apply);
143-
} else if (stage instanceof Stage.ProcessorStage) {
196+
}
197+
else if (stage instanceof Stage.ProcessorStage) {
144198
Processor<Object, Object> processor = (Processor) (((Stage.ProcessorStage) stage).getRsProcessor());
145199
Flow processorFlow;
146200
try {
147201
processorFlow = Flow.fromProcessor(() -> processor);
148-
} catch (Exception e) {
202+
}
203+
catch (Exception e) {
149204
// Technically can't happen, since the lambda we passed doesn't throw anything.
150205
throw new RuntimeException("Unexpected exception thrown", e);
151206
}
152207
return flow.via(processorFlow);
153-
} else if (stage.hasInlet() && stage.hasOutlet()) {
208+
}
209+
else if (stage.hasInlet() && stage.hasOutlet()) {
154210
throw new UnsupportedStageException(stage);
155-
} else {
211+
}
212+
else {
156213
throw new IllegalStateException("Got " + stage + " but needed a stage with an inlet and an outlet.");
157214
}
158215
}
159216

160217
private Sink toSink(Stage stage) {
161218
if (stage == Stage.FindFirst.INSTANCE) {
162219
return Sink.headOption();
163-
} else if (stage instanceof Stage.Collect) {
220+
}
221+
else if (stage instanceof Stage.Collect) {
164222
Collector collector = ((Stage.Collect) stage).getCollector();
165223
BiConsumer accumulator = collector.accumulator();
166224
Object firstContainer = collector.supplier().get();
@@ -170,31 +228,38 @@ private Sink toSink(Stage stage) {
170228
Sink<Object, CompletionStage<Object>> sink = Sink.fold(firstContainer, (resultContainer, in) -> {
171229
if (resultContainer == NULL) {
172230
accumulator.accept(null, in);
173-
} else {
231+
}
232+
else {
174233
accumulator.accept(resultContainer, in);
175234
}
176235
return resultContainer;
177236
});
178237
if (collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH) && firstContainer != NULL) {
179238
return sink;
180-
} else {
239+
}
240+
else {
181241
return sink.mapMaterializedValue(result -> result.thenApply(r -> {
182242
if (r == NULL) {
183243
return collector.finisher().apply(null);
184-
} else {
244+
}
245+
else {
185246
return collector.finisher().apply(r);
186247
}
187248
}));
188249
}
189-
} else if (stage instanceof Stage.SubscriberStage) {
250+
}
251+
else if (stage instanceof Stage.SubscriberStage) {
190252
return Flow.create()
191253
.viaMat(new TerminationWatcher(), Keep.right())
192254
.to((Sink) Sink.fromSubscriber(((Stage.SubscriberStage) stage).getRsSubscriber()));
193-
} else if (stage == Stage.Cancel.INSTANCE) {
255+
}
256+
else if (stage == Stage.Cancel.INSTANCE) {
194257
return Sink.cancelled().mapMaterializedValue(n -> CompletableFuture.completedFuture(null));
195-
} else if (stage.hasInlet() && !stage.hasOutlet()) {
258+
}
259+
else if (stage.hasInlet() && !stage.hasOutlet()) {
196260
throw new UnsupportedStageException(stage);
197-
} else {
261+
}
262+
else {
198263
throw new IllegalStateException("Got " + stage + " but needed a stage with an inlet and no outlet.");
199264
}
200265
}
@@ -207,22 +272,28 @@ private Source toSource(Stage stage) {
207272
int size = ((Collection) elements).size();
208273
if (size == 0) {
209274
return Source.empty();
210-
} else if (size == 1) {
275+
}
276+
else if (size == 1) {
211277
return Source.single(elements.iterator().next());
212278
}
213279
}
214280
return Source.from(elements);
215-
} else if (stage instanceof Stage.PublisherStage) {
281+
}
282+
else if (stage instanceof Stage.PublisherStage) {
216283
return Source.fromPublisher(((Stage.PublisherStage) stage).getRsPublisher());
217-
} else if (stage instanceof Stage.Concat) {
284+
}
285+
else if (stage instanceof Stage.Concat) {
218286
Graph first = ((Stage.Concat) stage).getFirst();
219287
Graph second = ((Stage.Concat) stage).getSecond();
220288
return buildSource(first).concat(buildSource(second));
221-
} else if (stage instanceof Stage.Failed) {
289+
}
290+
else if (stage instanceof Stage.Failed) {
222291
return Source.failed(((Stage.Failed) stage).getError());
223-
} else if (stage.hasOutlet() && !stage.hasInlet()) {
292+
}
293+
else if (stage.hasOutlet() && !stage.hasInlet()) {
224294
throw new UnsupportedStageException(stage);
225-
} else {
295+
}
296+
else {
226297
throw new IllegalStateException("Got " + stage + " but needed a stage with an outlet and no inlet.");
227298
}
228299
}
@@ -233,7 +304,7 @@ private <T> T materialize(RunnableGraph<T> graph) {
233304

234305
/**
235306
* This attribute does nothing except ensures a reference to this AkkaEngine is kept by the running stream.
236-
*
307+
* <p>
237308
* This is to prevent the cleaner used in the AkkaEngineProvider from finding that the AkkaEngine is unreachable
238309
* while a stream is still running, and shut the engine down. Once all streams stop running, the stream actor will
239310
* be disposed and the engine will become unreachable (as long as no user code references it), then it can be shut

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ buildscript {
1717
}
1818

1919
ext.libraries = [
20-
microprofile_reactive_streams_api: "org.eclipse.microprofile.reactive.streams:microprofile-reactive-streams-api:1.0-alpha-jroper-1",
21-
microprofile_reactive_streams_tck: "org.eclipse.microprofile.reactive.streams:microprofile-reactive-streams-tck:1.0-alpha-jroper-1"
20+
microprofile_reactive_streams_api: "org.eclipse.microprofile.reactive.streams:microprofile-reactive-streams-api:1.0-alpha-jroper-2",
21+
microprofile_reactive_streams_tck: "org.eclipse.microprofile.reactive.streams:microprofile-reactive-streams-tck:1.0-alpha-jroper-2"
2222
]
2323

2424
allprojects {

lightbend-microprofile-reactive-streams.iml

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)