From 53aba050d5e241fe8a81edba802be4c7ac32e8b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Karnok?= Date: Wed, 12 Jul 2017 23:10:10 +0200 Subject: [PATCH 1/6] Bridge between Reactive-Streams and JDK 9 Flow API --- .travis.yml | 28 +- build.gradle | 6 +- flow-bridge/.gitignore | 1 + flow-bridge/build.gradle | 9 + .../reactivestreams/ReactiveFlowBridge.java | 357 +++++++++++++++++ .../reactivestreams/MulticastPublisher.java | 358 ++++++++++++++++++ .../ReactiveFlowBridgeTest.java | 113 ++++++ .../SubmissionPublisherTckTest.java | 59 +++ .../org/reactivestreams/TestConsumer.java | 163 ++++++++ gradle/wrapper/gradle-wrapper.jar | Bin 50557 -> 53639 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 10 +- gradlew.bat | 2 +- settings.gradle | 20 +- 14 files changed, 1113 insertions(+), 17 deletions(-) create mode 100644 flow-bridge/.gitignore create mode 100644 flow-bridge/build.gradle create mode 100644 flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java create mode 100644 flow-bridge/src/test/java/org/reactivestreams/MulticastPublisher.java create mode 100644 flow-bridge/src/test/java/org/reactivestreams/ReactiveFlowBridgeTest.java create mode 100644 flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java create mode 100644 flow-bridge/src/test/java/org/reactivestreams/TestConsumer.java diff --git a/.travis.yml b/.travis.yml index 1a2d1004..8da6be74 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,35 @@ language: java sudo: required +dist: trusty +#group: edge + script: - ./gradlew check cache: directories: - $HOME/.gradle -jdk: - - oraclejdk8 - - openjdk6 + +before_install: + - export GRADLE_OPTS=-Xmx1024m + +matrix: + include: + - jdk: openjdk7 + - jdk: oraclejdk8 # JDK 1.8.0_131-b11 + - jdk: oraclejdk9 # JDK 9-ea+174 provided by Travis will be replaced by 9+177 in "before_install" + before_install: + - cd ~ + - wget http://download.java.net/java/jdk9/archive/177/binaries/jdk-9+177_linux-x64_bin.tar.gz + - tar -xzf jdk-9+177_linux-x64_bin.tar.gz + - export JAVA_HOME=~/jdk-9 + - PATH=$JAVA_HOME/bin:$PATH + - cd - + +# Don't let Travis CI execute './gradlew assemble' by default +install: +# Display Gradle, JVM and other versions + - ./gradlew -version + env: global: - TERM=dumb diff --git a/build.gradle b/build.gradle index f57c047a..0de903bf 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ subprojects { apply plugin: "osgi" group = "org.reactivestreams" - version = "1.0.1-RC2" + version = "1.0.0" sourceCompatibility = 1.6 targetCompatibility = 1.6 @@ -42,11 +42,11 @@ subprojects { instructionReplace "Bundle-Vendor", "Reactive Streams SIG" instructionReplace "Bundle-Description", "Reactive Streams API" instructionReplace "Bundle-DocURL", "http://reactive-streams.org" - instructionReplace "Bundle-Version", "1.0.1-RC2" + instructionReplace "Bundle-Version", "1.0.0" } } - if (name in ["reactive-streams", "reactive-streams-tck", "reactive-streams-examples"]) { + if (name in ["reactive-streams", "reactive-streams-tck", "reactive-streams-examples", "reactive-streams-flow-bridge"]) { apply plugin: "maven" apply plugin: "signing" diff --git a/flow-bridge/.gitignore b/flow-bridge/.gitignore new file mode 100644 index 00000000..7447f89a --- /dev/null +++ b/flow-bridge/.gitignore @@ -0,0 +1 @@ +/bin \ No newline at end of file diff --git a/flow-bridge/build.gradle b/flow-bridge/build.gradle new file mode 100644 index 00000000..036fedc6 --- /dev/null +++ b/flow-bridge/build.gradle @@ -0,0 +1,9 @@ +description = 'reactive-streams-flow-bridge' + +dependencies { + compile project(':reactive-streams') + + testCompile project(':reactive-streams-tck') + testCompile group: 'org.testng', name: 'testng', version: '5.14.10' +} +test.useTestNG() \ No newline at end of file diff --git a/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java b/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java new file mode 100644 index 00000000..b56b02bc --- /dev/null +++ b/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java @@ -0,0 +1,357 @@ +/************************************************************************ + * Licensed under Public Domain (CC0) * + * * + * To the extent possible under law, the person who associated CC0 with * + * this code has waived all copyright and related or neighboring * + * rights to this code. * + * * + * You should have received a copy of the CC0 legalcode along with this * + * work. If not, see .* + ************************************************************************/ + +package org.reactivestreams; + +import java.util.concurrent.Flow; + +/** + * Bridge between Reactive-Streams API and the Java 9 Flow API. + */ +public final class ReactiveFlowBridge { + /** Utility class. */ + private ReactiveFlowBridge() { + throw new IllegalStateException("No instances!"); + } + + /** + * Converts a Flow Publisher into a Reactive Publisher. + * @param the value type + * @param flowPublisher the source Flow Publisher to convert + * @return the equivalent Reactive Publisher + */ + @SuppressWarnings("unchecked") + public static org.reactivestreams.Publisher toReactive( + Flow.Publisher flowPublisher) { + if (flowPublisher == null) { + throw new NullPointerException("flowPublisher"); + } + if (flowPublisher instanceof org.reactivestreams.Publisher) { + return (org.reactivestreams.Publisher)flowPublisher; + } + if (flowPublisher instanceof FlowPublisherFromReactive) { + return (org.reactivestreams.Publisher)(((FlowPublisherFromReactive)flowPublisher).reactive); + } + return new ReactivePublisherFromFlow(flowPublisher); + } + + /** + * Converts a Reactive Publisher into a Flow Publisher. + * @param the value type + * @param reactivePublisher the source Reactive Publisher to convert + * @return the equivalent Flow Publisher + */ + @SuppressWarnings("unchecked") + public static Flow.Publisher toFlow( + org.reactivestreams.Publisher reactivePublisher + ) { + if (reactivePublisher == null) { + throw new NullPointerException("reactivePublisher"); + } + if (reactivePublisher instanceof Flow.Publisher) { + return (Flow.Publisher)reactivePublisher; + } + if (reactivePublisher instanceof ReactivePublisherFromFlow) { + return (Flow.Publisher)(((ReactivePublisherFromFlow)reactivePublisher).flow); + } + return new FlowPublisherFromReactive(reactivePublisher); + } + + /** + * Converts a Flow Processor into a Reactive Processor. + * @param the input value type + * @param the output value type + * @param flowProcessor the source Flow Processor to convert + * @return the equivalent Reactive Processor + */ + @SuppressWarnings("unchecked") + public static org.reactivestreams.Processor toReactive( + Flow.Processor flowProcessor + ) { + if (flowProcessor == null) { + throw new NullPointerException("flowProcessor"); + } + if (flowProcessor instanceof org.reactivestreams.Processor) { + return (org.reactivestreams.Processor)flowProcessor; + } + if (flowProcessor instanceof FlowToReactiveProcessor) { + return (org.reactivestreams.Processor)(((FlowToReactiveProcessor)flowProcessor).reactive); + } + return new ReactiveToFlowProcessor(flowProcessor); + } + + /** + * Converts a Reactive Processor into a Flow Processor. + * @param the input value type + * @param the output value type + * @param reactiveProcessor the source Reactive Processor to convert + * @return the equivalent Flow Processor + */ + @SuppressWarnings("unchecked") + public static Flow.Processor toFlow( + org.reactivestreams.Processor reactiveProcessor + ) { + if (reactiveProcessor == null) { + throw new NullPointerException("reactiveProcessor"); + } + if (reactiveProcessor instanceof Flow.Processor) { + return (Flow.Processor)reactiveProcessor; + } + if (reactiveProcessor instanceof ReactiveToFlowProcessor) { + return (Flow.Processor)(((ReactiveToFlowProcessor)reactiveProcessor).flow); + } + return new FlowToReactiveProcessor(reactiveProcessor); + } + + /** + * Wraps a Reactive Subscription and converts the calls to a Flow Subscription. + */ + static final class FlowToReactiveSubscription implements Flow.Subscription { + private final org.reactivestreams.Subscription reactive; + + public FlowToReactiveSubscription(org.reactivestreams.Subscription reactive) { + this.reactive = reactive; + } + + @Override + public void request(long n) { + reactive.request(n); + } + + @Override + public void cancel() { + reactive.cancel(); + } + + } + + /** + * Wraps a Flow Subscription and converts the calls to a Reactive Subscription. + */ + static final class ReactiveToFlowSubscription implements org.reactivestreams.Subscription { + private final Flow.Subscription flow; + + public ReactiveToFlowSubscription(Flow.Subscription flow) { + this.flow = flow; + } + + @Override + public void request(long n) { + flow.request(n); + } + + @Override + public void cancel() { + flow.cancel(); + } + + + } + + /** + * Wraps a Reactive Subscriber and forwards methods of the Flow Subscriber to it. + * @param the value type + */ + static final class FlowToReactiveSubscriber + implements Flow.Subscriber { + private final org.reactivestreams.Subscriber reactive; + + public FlowToReactiveSubscriber(org.reactivestreams.Subscriber reactive) { + this.reactive = reactive; + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + reactive.onSubscribe(new ReactiveToFlowSubscription(subscription)); + } + + @Override + public void onNext(T item) { + reactive.onNext(item); + } + + @Override + public void onError(Throwable throwable) { + reactive.onError(throwable); + } + + @Override + public void onComplete() { + reactive.onComplete(); + } + + } + + /** + * Wraps a Reactive Subscriber and forwards methods of the Flow Subscriber to it. + * @param the value type + */ + static final class ReactiveToFlowSubscriber + implements org.reactivestreams.Subscriber { + private final Flow.Subscriber flow; + + public ReactiveToFlowSubscriber(Flow.Subscriber flow) { + this.flow = flow; + } + + @Override + public void onSubscribe(org.reactivestreams.Subscription subscription) { + flow.onSubscribe(new FlowToReactiveSubscription(subscription)); + } + + @Override + public void onNext(T item) { + flow.onNext(item); + } + + @Override + public void onError(Throwable throwable) { + flow.onError(throwable); + } + + @Override + public void onComplete() { + flow.onComplete(); + } + + } + + /** + * Wraps a Flow Processor and forwards methods of the Reactive Processor to it. + * @param the input type + * @param the output type + */ + static final class ReactiveToFlowProcessor + implements org.reactivestreams.Processor { + private final Flow.Processor flow; + + public ReactiveToFlowProcessor(Flow.Processor flow) { + this.flow = flow; + } + + @Override + public void onSubscribe(org.reactivestreams.Subscription s) { + flow.onSubscribe(new FlowToReactiveSubscription(s)); + } + + @Override + public void onNext(T t) { + flow.onNext(t); + } + + @Override + public void onError(Throwable t) { + flow.onError(t); + } + + @Override + public void onComplete() { + flow.onComplete(); + } + + @Override + public void subscribe(org.reactivestreams.Subscriber s) { + if (s == null) { + flow.subscribe(null); + return; + } + flow.subscribe(new FlowToReactiveSubscriber(s)); + } + } + + /** + * Wraps a Reactive Processor and forwards methods of the Flow Processor to it. + * @param the input type + * @param the output type + */ + static final class FlowToReactiveProcessor + implements Flow.Processor { + private final org.reactivestreams.Processor reactive; + + public FlowToReactiveProcessor(org.reactivestreams.Processor reactive) { + this.reactive = reactive; + } + + @Override + public void onSubscribe(Flow.Subscription s) { + reactive.onSubscribe(new ReactiveToFlowSubscription(s)); + } + + @Override + public void onNext(T t) { + reactive.onNext(t); + } + + @Override + public void onError(Throwable t) { + reactive.onError(t); + } + + @Override + public void onComplete() { + reactive.onComplete(); + } + + @Override + public void subscribe(Flow.Subscriber s) { + if (s == null) { + reactive.subscribe(null); + return; + } + reactive.subscribe(new ReactiveToFlowSubscriber(s)); + } + } + + /** + * Reactive Publisher that wraps a Flow Publisher. + * @param the value type + */ + static final class ReactivePublisherFromFlow implements org.reactivestreams.Publisher { + + final Flow.Publisher flow; + + public ReactivePublisherFromFlow(Flow.Publisher flowPublisher) { + this.flow = flowPublisher; + } + + @Override + public void subscribe(org.reactivestreams.Subscriber reactive) { + if (reactive == null) { + flow.subscribe(null); + return; + } + flow.subscribe(new FlowToReactiveSubscriber(reactive)); + } + } + + /** + * Flow Publisher that wraps a Reactive Publisher. + * @param the value type + */ + static final class FlowPublisherFromReactive implements Flow.Publisher { + + final org.reactivestreams.Publisher reactive; + + public FlowPublisherFromReactive(org.reactivestreams.Publisher reactivePublisher) { + this.reactive = reactivePublisher; + } + + @Override + public void subscribe(Flow.Subscriber flow) { + if (flow == null) { + reactive.subscribe(null); + return; + } + reactive.subscribe(new ReactiveToFlowSubscriber(flow)); + } + } + +} \ No newline at end of file diff --git a/flow-bridge/src/test/java/org/reactivestreams/MulticastPublisher.java b/flow-bridge/src/test/java/org/reactivestreams/MulticastPublisher.java new file mode 100644 index 00000000..31f00543 --- /dev/null +++ b/flow-bridge/src/test/java/org/reactivestreams/MulticastPublisher.java @@ -0,0 +1,358 @@ +/************************************************************************ + * Licensed under Public Domain (CC0) * + * * + * To the extent possible under law, the person who associated CC0 with * + * this code has waived all copyright and related or neighboring * + * rights to this code. * + * * + * You should have received a copy of the CC0 legalcode along with this * + * work. If not, see .* + ************************************************************************/ + +package org.reactivestreams; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.concurrent.Flow; +import java.util.concurrent.Flow.*; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.atomic.*; + +final class MulticastPublisher implements Publisher, AutoCloseable { + + final Executor executor; + final int bufferSize; + + final AtomicBoolean done = new AtomicBoolean(); + Throwable error; + + static final InnerSubscription[] EMPTY = new InnerSubscription[0]; + static final InnerSubscription[] TERMINATED = new InnerSubscription[0]; + + + final AtomicReference[]> subscribers = new AtomicReference[]>(); + + public MulticastPublisher() { + this(ForkJoinPool.commonPool(), Flow.defaultBufferSize()); + } + + @SuppressWarnings("unchecked") + public MulticastPublisher(Executor executor, int bufferSize) { + if ((bufferSize & (bufferSize - 1)) != 0) { + throw new IllegalArgumentException("Please provide a power-of-two buffer size"); + } + this.executor = executor; + this.bufferSize = bufferSize; + subscribers.setRelease(EMPTY); + } + + public boolean offer(T item) { + Objects.requireNonNull(item, "item is null"); + + InnerSubscription[] a = subscribers.get(); + synchronized (this) { + for (InnerSubscription inner : a) { + if (inner.isFull()) { + return false; + } + } + for (InnerSubscription inner : a) { + inner.offer(item); + inner.drain(executor); + } + } + + return true; + } + + @SuppressWarnings("unchecked") + public void complete() { + if (done.compareAndSet(false, true)) { + for (InnerSubscription inner : subscribers.getAndSet(TERMINATED)) { + inner.done = true; + inner.drain(executor); + } + } + } + + @SuppressWarnings("unchecked") + public void completeExceptionally(Throwable error) { + if (done.compareAndSet(false, true)) { + this.error = error; + for (InnerSubscription inner : subscribers.getAndSet(TERMINATED)) { + inner.error = error; + inner.done = true; + inner.drain(executor); + } + } + } + + @Override + public void close() { + complete(); + } + + @Override + public void subscribe(Subscriber subscriber) { + Objects.requireNonNull(subscriber, "subscriber is null"); + InnerSubscription inner = new InnerSubscription(subscriber, bufferSize, this); + if (!add(inner)) { + Throwable ex = error; + if (ex != null) { + inner.error = ex; + } + inner.done = true; + } + inner.drain(executor); + } + + public boolean hasSubscribers() { + return subscribers.get().length != 0; + } + + boolean add(InnerSubscription inner) { + + for (;;) { + InnerSubscription[] a = subscribers.get(); + if (a == TERMINATED) { + return false; + } + + int n = a.length; + @SuppressWarnings("unchecked") + InnerSubscription[] b = new InnerSubscription[n + 1]; + System.arraycopy(a, 0, b, 0, n); + b[n] = inner; + if (subscribers.compareAndSet(a, b)) { + return true; + } + } + } + + @SuppressWarnings("unchecked") + void remove(InnerSubscription inner) { + for (;;) { + InnerSubscription[] a = subscribers.get(); + int n = a.length; + if (n == 0) { + break; + } + + int j = -1; + for (int i = 0; i < n; i++) { + if (a[i] == inner) { + j = i; + break; + } + } + if (j < 0) { + break; + } + InnerSubscription[] b; + if (n == 1) { + b = EMPTY; + } else { + b = new InnerSubscription[n - 1]; + System.arraycopy(a, 0, b, 0, j); + System.arraycopy(a, j + 1, b, j, n - j - 1); + } + if (subscribers.compareAndSet(a, b)) { + break; + } + } + } + + static final class InnerSubscription implements Subscription, Runnable { + + final Subscriber actual; + final MulticastPublisher parent; + final AtomicReferenceArray queue; + final int mask; + + volatile boolean badRequest; + final AtomicBoolean cancelled = new AtomicBoolean(); + + volatile boolean done; + Throwable error; + + boolean subscribed; + long emitted; + + final AtomicLong requested = new AtomicLong(); + + final AtomicInteger wip = new AtomicInteger(); + + final AtomicLong producerIndex = new AtomicLong(); + + final AtomicLong consumerIndex = new AtomicLong(); + + InnerSubscription(Subscriber actual, int bufferSize, MulticastPublisher parent) { + this.actual = actual; + this.queue = new AtomicReferenceArray(bufferSize); + this.parent = parent; + this.mask = bufferSize - 1; + } + + void offer(T item) { + AtomicReferenceArray q = queue; + int m = mask; + long pi = producerIndex.get(); + int offset = (int)(pi) & m; + + q.setRelease(offset, item); + producerIndex.setRelease(pi + 1); + } + + T poll() { + AtomicReferenceArray q = queue; + int m = mask; + long ci = consumerIndex.get(); + + int offset = (int)(ci) & m; + T o = q.getAcquire(offset); + if (o != null) { + q.setRelease(offset, null); + consumerIndex.setRelease(ci + 1); + } + return o; + } + + boolean isFull() { + return 1 + mask + consumerIndex.get() == producerIndex.get(); + } + + void drain(Executor executor) { + if (wip.getAndAdd(1) == 0) { + executor.execute(this); + } + } + + @Override + public void request(long n) { + if (n <= 0L) { + badRequest = true; + done = true; + } else { + for (;;) { + long r = requested.get(); + long u = r + n; + if (u < 0) { + u = Long.MAX_VALUE; + } + if (requested.compareAndSet(r, u)) { + break; + } + } + } + drain(parent.executor); + } + + @Override + public void cancel() { + if (cancelled.compareAndSet(false, true)) { + parent.remove(this); + } + } + + void clear() { + error = null; + while (poll() != null) ; + } + + @Override + public void run() { + int missed = 1; + Subscriber a = actual; + + outer: + for (;;) { + + if (subscribed) { + if (cancelled.get()) { + clear(); + } else { + long r = requested.get(); + long e = emitted; + + while (e != r) { + if (cancelled.get()) { + continue outer; + } + + boolean d = done; + + if (d) { + Throwable ex = error; + if (ex != null) { + cancelled.setRelease(true); + a.onError(ex); + continue outer; + } + if (badRequest) { + cancelled.setRelease(true); + parent.remove(this); + a.onError(new IllegalArgumentException("§3.9 violated: request was not positive")); + continue outer; + } + } + + T v = poll(); + boolean empty = v == null; + + if (d && empty) { + cancelled.setRelease(true); + a.onComplete(); + break; + } + + if (empty) { + break; + } + + a.onNext(v); + + e++; + } + + if (e == r) { + if (cancelled.get()) { + continue outer; + } + if (done) { + Throwable ex = error; + if (ex != null) { + cancelled.setRelease(true); + a.onError(ex); + } else + if (badRequest) { + cancelled.setRelease(true); + a.onError(new IllegalArgumentException("§3.9 violated: request was not positive")); + } else + if (producerIndex == consumerIndex) { + cancelled.setRelease(true); + a.onComplete(); + } + } + } + + emitted = e; + } + } else { + subscribed = true; + a.onSubscribe(this); + } + + int w = wip.get(); + if (missed == w) { + w = wip.getAndAdd(-missed); + if (missed == w) { + break; + } + } + missed = w; + } + } + } +} \ No newline at end of file diff --git a/flow-bridge/src/test/java/org/reactivestreams/ReactiveFlowBridgeTest.java b/flow-bridge/src/test/java/org/reactivestreams/ReactiveFlowBridgeTest.java new file mode 100644 index 00000000..fd6675fa --- /dev/null +++ b/flow-bridge/src/test/java/org/reactivestreams/ReactiveFlowBridgeTest.java @@ -0,0 +1,113 @@ +/************************************************************************ + * Licensed under Public Domain (CC0) * + * * + * To the extent possible under law, the person who associated CC0 with * + * this code has waived all copyright and related or neighboring * + * rights to this code. * + * * + * You should have received a copy of the CC0 legalcode along with this * + * work. If not, see .* + ************************************************************************/ + +package org.reactivestreams; + +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.concurrent.Executor; +import java.util.concurrent.Flow; +import java.util.concurrent.SubmissionPublisher; + +public class ReactiveFlowBridgeTest { + @Test + public void reactiveToFlowNormal() { + MulticastPublisher p = new MulticastPublisher(new Executor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }, Flow.defaultBufferSize()); + + TestConsumer tc = new TestConsumer(); + + ReactiveFlowBridge.toFlow(p).subscribe(tc); + + p.offer(1); + p.offer(2); + p.offer(3); + p.offer(4); + p.offer(5); + p.complete(); + + tc.assertRange(1, 5); + } + + @Test + public void reactiveToFlowError() { + MulticastPublisher p = new MulticastPublisher(new Executor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }, Flow.defaultBufferSize()); + + TestConsumer tc = new TestConsumer(); + + ReactiveFlowBridge.toFlow(p).subscribe(tc); + + p.offer(1); + p.offer(2); + p.offer(3); + p.offer(4); + p.offer(5); + p.completeExceptionally(new IOException()); + + tc.assertFailure(IOException.class, 1, 2, 3, 4, 5); + } + + @Test + public void flowToReactiveNormal() { + SubmissionPublisher p = new SubmissionPublisher(new Executor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }, Flow.defaultBufferSize()); + + TestConsumer tc = new TestConsumer(); + + ReactiveFlowBridge.toReactive(p).subscribe(tc); + + p.submit(1); + p.submit(2); + p.submit(3); + p.submit(4); + p.submit(5); + p.close(); + + tc.assertRange(1, 5); + } + + @Test + public void flowToReactiveError() { + SubmissionPublisher p = new SubmissionPublisher(new Executor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }, Flow.defaultBufferSize()); + + TestConsumer tc = new TestConsumer(); + + ReactiveFlowBridge.toReactive(p).subscribe(tc); + + p.submit(1); + p.submit(2); + p.submit(3); + p.submit(4); + p.submit(5); + p.closeExceptionally(new IOException()); + + tc.assertFailure(IOException.class, 1, 2, 3, 4, 5); + } +} diff --git a/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java b/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java new file mode 100644 index 00000000..b808237b --- /dev/null +++ b/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java @@ -0,0 +1,59 @@ +/************************************************************************ + * Licensed under Public Domain (CC0) * + * * + * To the extent possible under law, the person who associated CC0 with * + * this code has waived all copyright and related or neighboring * + * rights to this code. * + * * + * You should have received a copy of the CC0 legalcode along with this * + * work. If not, see .* + ************************************************************************/ + +package org.reactivestreams; + +import org.reactivestreams.Publisher; +import org.reactivestreams.tck.PublisherVerification; +import org.reactivestreams.tck.TestEnvironment; +import org.testng.annotations.Test; + +import java.util.Objects; +import java.util.concurrent.Flow; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.SubmissionPublisher; + +@Test +public class SubmissionPublisherTckTest extends PublisherVerification { + + public SubmissionPublisherTckTest() { + super(new TestEnvironment(300)); + } + + @Override + public Publisher createPublisher(final long elements) { + final SubmissionPublisher sp = new SubmissionPublisher(); + new Thread(new Runnable() { + @Override + public void run() { + while (!sp.hasSubscribers()) { + Thread.yield(); + } + for (int i = 0; i < elements; i++) { + sp.submit(i); + } + sp.close(); + } + }).start(); + return ReactiveFlowBridge.toReactive(sp); + } + + @Override + public Publisher createFailedPublisher() { + return null; + } + + @Override + public long maxElementsFromPublisher() { + return 100; + } + +} \ No newline at end of file diff --git a/flow-bridge/src/test/java/org/reactivestreams/TestConsumer.java b/flow-bridge/src/test/java/org/reactivestreams/TestConsumer.java new file mode 100644 index 00000000..7ee479cc --- /dev/null +++ b/flow-bridge/src/test/java/org/reactivestreams/TestConsumer.java @@ -0,0 +1,163 @@ +/************************************************************************ + * Licensed under Public Domain (CC0) * + * * + * To the extent possible under law, the person who associated CC0 with * + * this code has waived all copyright and related or neighboring * + * rights to this code. * + * * + * You should have received a copy of the CC0 legalcode along with this * + * work. If not, see .* + ************************************************************************/ + +package org.reactivestreams; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Flow; +import java.util.concurrent.TimeUnit; + +class TestConsumer implements Flow.Subscriber, Subscriber { + + protected final List values; + + protected final List errors; + + protected int completions; + + protected Flow.Subscription subscription; + + protected Subscription subscriptionRs; + + protected final CountDownLatch done; + + final long initialRequest; + + public TestConsumer() { + this(Long.MAX_VALUE); + } + + public TestConsumer(long initialRequest) { + this.values = new ArrayList(); + this.errors = new ArrayList(); + this.done = new CountDownLatch(1); + this.initialRequest = initialRequest; + } + + @Override + public final void onSubscribe(Flow.Subscription s) { + this.subscription = s; + s.request(initialRequest); + } + + @Override + public void onSubscribe(Subscription s) { + this.subscriptionRs = s; + s.request(initialRequest); + } + + @Override + public void onNext(T item) { + values.add(item); + if (subscription == null && subscriptionRs == null) { + errors.add(new IllegalStateException("onSubscribe not called")); + } + } + + @Override + public void onError(Throwable throwable) { + errors.add(throwable); + if (subscription == null && subscriptionRs == null) { + errors.add(new IllegalStateException("onSubscribe not called")); + } + done.countDown(); + } + + @Override + public void onComplete() { + completions++; + if (subscription == null && subscriptionRs == null) { + errors.add(new IllegalStateException("onSubscribe not called")); + } + done.countDown(); + } + + public final void cancel() { + // FIXME implement deferred cancellation + } + + public final List values() { + return values; + } + + public final List errors() { + return errors; + } + + public final int completions() { + return completions; + } + + public final boolean await(long timeout, TimeUnit unit) throws InterruptedException { + return done.await(timeout, unit); + } + + public final TestConsumer assertResult(T... items) { + if (!values.equals(Arrays.asList(items))) { + throw new AssertionError("Expected: " + Arrays.toString(items) + ", Actual: " + values + ", Completions: " + completions); + } + if (completions != 1) { + throw new AssertionError("Not completed: " + completions); + } + return this; + } + + + public final TestConsumer assertFailure(Class errorClass, T... items) { + if (!values.equals(Arrays.asList(items))) { + throw new AssertionError("Expected: " + Arrays.toString(items) + ", Actual: " + values + ", Completions: " + completions); + } + if (completions != 0) { + throw new AssertionError("Completed: " + completions); + } + if (errors.isEmpty()) { + throw new AssertionError("No errors"); + } + if (!errorClass.isInstance(errors.get(0))) { + AssertionError ae = new AssertionError("Wrong throwable"); + ae.initCause(errors.get(0)); + throw ae; + } + return this; + } + + public final TestConsumer awaitDone(long timeout, TimeUnit unit) { + try { + if (!done.await(timeout, unit)) { + subscription.cancel(); + throw new RuntimeException("Timed out. Values: " + values.size() + + ", Errors: " + errors.size() + ", Completions: " + completions); + } + } catch (InterruptedException ex) { + throw new RuntimeException("Interrupted"); + } + return this; + } + + public final TestConsumer assertRange(int start, int count) { + if (values.size() != count) { + throw new AssertionError("Expected: " + count + ", Actual: " + values.size()); + } + for (int i = 0; i < count; i++) { + if ((Integer)values.get(i) != start + i) { + throw new AssertionError("Index: " + i + ", Expected: " + + (i + start) + ", Actual: " +values.get(i)); + } + } + if (completions != 1) { + throw new AssertionError("Not completed: " + completions); + } + return this; + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5838598129719e795cc5633f411468bdec311016..2c6137b87896c8f70315ae454e00a969ef5f6019 100644 GIT binary patch delta 41536 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 H%U}Nom-dUg delta 38422 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 SJ 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 rg7ZiKnwMM&- +cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" -cd "$SAVED" >&- +cd "$SAVED" >/dev/null CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -114,6 +109,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` diff --git a/gradlew.bat b/gradlew.bat index 8a0b282a..5f192121 100755 --- a/gradlew.bat +++ b/gradlew.bat @@ -46,7 +46,7 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args diff --git a/settings.gradle b/settings.gradle index 61cdbf6d..01529608 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,9 +1,27 @@ rootProject.name = 'reactive-streams' + +def jdkFlow = false; + +try { + Class.forName("java.util.concurrent.Flow"); + jdkFlow = true; + println("Java 9 Flow API found") +} catch (Throwable ex) { + // Flow API not available + println("Java 9 Flow API not available") +} + include ':reactive-streams' include ':reactive-streams-tck' include ':reactive-streams-examples' +if (jdkFlow) { + include ':reactive-streams-flow-bridge' +} + project(':reactive-streams').projectDir = "$rootDir/api" as File project(':reactive-streams-tck').projectDir = "$rootDir/tck" as File project(':reactive-streams-examples').projectDir = "$rootDir/examples" as File - +if (jdkFlow) { + project(':reactive-streams-flow-bridge').projectDir = "$rootDir/flow-bridge" as File +} From 058b3a40de33e7e9b2c5e451533383ad2960db0b Mon Sep 17 00:00:00 2001 From: akarnokd Date: Thu, 13 Jul 2017 10:20:26 +0200 Subject: [PATCH 2/6] Apply changes based on ktoso's feedback --- .travis.yml | 2 ++ build.gradle | 2 +- flow-bridge/build.gradle | 6 +++++- .../java/org/reactivestreams/ReactiveFlowBridge.java | 12 ++++++------ .../reactivestreams/SubmissionPublisherTckTest.java | 5 ++++- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8da6be74..f7cf2409 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ matrix: include: - jdk: openjdk7 - jdk: oraclejdk8 # JDK 1.8.0_131-b11 + # From https://github.com/reactive-streams/reactive-streams-jvm/pull/383 - jdk: oraclejdk9 # JDK 9-ea+174 provided by Travis will be replaced by 9+177 in "before_install" before_install: - cd ~ @@ -26,6 +27,7 @@ matrix: - cd - # Don't let Travis CI execute './gradlew assemble' by default +# From https://github.com/reactive-streams/reactive-streams-jvm/pull/383 install: # Display Gradle, JVM and other versions - ./gradlew -version diff --git a/build.gradle b/build.gradle index 0de903bf..ea95ed48 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ subprojects { apply plugin: "osgi" group = "org.reactivestreams" - version = "1.0.0" + version = "1.0.1-RC2" sourceCompatibility = 1.6 targetCompatibility = 1.6 diff --git a/flow-bridge/build.gradle b/flow-bridge/build.gradle index 036fedc6..13debe28 100644 --- a/flow-bridge/build.gradle +++ b/flow-bridge/build.gradle @@ -6,4 +6,8 @@ dependencies { testCompile project(':reactive-streams-tck') testCompile group: 'org.testng', name: 'testng', version: '5.14.10' } -test.useTestNG() \ No newline at end of file +test.useTestNG() + +javadoc { + options.links("http://download.java.net/java/jdk9/docs/api") +} \ No newline at end of file diff --git a/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java b/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java index b56b02bc..c27ec396 100644 --- a/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java +++ b/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java @@ -24,7 +24,7 @@ private ReactiveFlowBridge() { /** * Converts a Flow Publisher into a Reactive Publisher. - * @param the value type + * @param the element type * @param flowPublisher the source Flow Publisher to convert * @return the equivalent Reactive Publisher */ @@ -45,7 +45,7 @@ public static org.reactivestreams.Publisher toReactive( /** * Converts a Reactive Publisher into a Flow Publisher. - * @param the value type + * @param the element type * @param reactivePublisher the source Reactive Publisher to convert * @return the equivalent Flow Publisher */ @@ -158,7 +158,7 @@ public void cancel() { /** * Wraps a Reactive Subscriber and forwards methods of the Flow Subscriber to it. - * @param the value type + * @param the element type */ static final class FlowToReactiveSubscriber implements Flow.Subscriber { @@ -192,7 +192,7 @@ public void onComplete() { /** * Wraps a Reactive Subscriber and forwards methods of the Flow Subscriber to it. - * @param the value type + * @param the element type */ static final class ReactiveToFlowSubscriber implements org.reactivestreams.Subscriber { @@ -312,7 +312,7 @@ public void subscribe(Flow.Subscriber s) { /** * Reactive Publisher that wraps a Flow Publisher. - * @param the value type + * @param the element type */ static final class ReactivePublisherFromFlow implements org.reactivestreams.Publisher { @@ -334,7 +334,7 @@ public void subscribe(org.reactivestreams.Subscriber reactive) { /** * Flow Publisher that wraps a Reactive Publisher. - * @param the value type + * @param the element type */ static final class FlowPublisherFromReactive implements Flow.Publisher { diff --git a/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java b/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java index b808237b..e8261d8c 100644 --- a/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java +++ b/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java @@ -16,6 +16,7 @@ import org.reactivestreams.tck.TestEnvironment; import org.testng.annotations.Test; +import java.io.IOException; import java.util.Objects; import java.util.concurrent.Flow; import java.util.concurrent.ForkJoinPool; @@ -48,7 +49,9 @@ public void run() { @Override public Publisher createFailedPublisher() { - return null; + final SubmissionPublisher sp = new SubmissionPublisher(); + sp.closeExceptionally(new IOException()); + return ReactiveFlowBridge.toReactive(sp); } @Override From 29bc52362c68ca65d3f635055d9fff9526fd6566 Mon Sep 17 00:00:00 2001 From: akarnokd Date: Fri, 22 Sep 2017 13:55:52 +0200 Subject: [PATCH 3/6] Use oraclejdk9, resolve build.gradle conflict --- .travis.yml | 10 +--------- build.gradle | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index f7cf2409..4e945266 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,15 +16,7 @@ matrix: include: - jdk: openjdk7 - jdk: oraclejdk8 # JDK 1.8.0_131-b11 - # From https://github.com/reactive-streams/reactive-streams-jvm/pull/383 - - jdk: oraclejdk9 # JDK 9-ea+174 provided by Travis will be replaced by 9+177 in "before_install" - before_install: - - cd ~ - - wget http://download.java.net/java/jdk9/archive/177/binaries/jdk-9+177_linux-x64_bin.tar.gz - - tar -xzf jdk-9+177_linux-x64_bin.tar.gz - - export JAVA_HOME=~/jdk-9 - - PATH=$JAVA_HOME/bin:$PATH - - cd - + - jdk: oraclejdk9 # Don't let Travis CI execute './gradlew assemble' by default # From https://github.com/reactive-streams/reactive-streams-jvm/pull/383 diff --git a/build.gradle b/build.gradle index ea95ed48..d5440998 100644 --- a/build.gradle +++ b/build.gradle @@ -42,7 +42,7 @@ subprojects { instructionReplace "Bundle-Vendor", "Reactive Streams SIG" instructionReplace "Bundle-Description", "Reactive Streams API" instructionReplace "Bundle-DocURL", "http://reactive-streams.org" - instructionReplace "Bundle-Version", "1.0.0" + instructionReplace "Bundle-Version", "1.0.1" } } From 4d842b774d3a4edf8d0f11452b83d6856d3cd7b8 Mon Sep 17 00:00:00 2001 From: akarnokd Date: Fri, 22 Sep 2017 14:32:48 +0200 Subject: [PATCH 4/6] Change txt/code to use "Reactive Streams" as designator --- .../reactivestreams/ReactiveFlowBridge.java | 116 +++++++++--------- .../ReactiveFlowBridgeTest.java | 4 +- .../SubmissionPublisherTckTest.java | 8 +- 3 files changed, 62 insertions(+), 66 deletions(-) diff --git a/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java b/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java index c27ec396..27494d95 100644 --- a/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java +++ b/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java @@ -14,7 +14,7 @@ import java.util.concurrent.Flow; /** - * Bridge between Reactive-Streams API and the Java 9 Flow API. + * Bridge between Reactive Streams API and the Java 9 {@link java.util.concurrent.Flow} API. */ public final class ReactiveFlowBridge { /** Utility class. */ @@ -23,13 +23,13 @@ private ReactiveFlowBridge() { } /** - * Converts a Flow Publisher into a Reactive Publisher. + * Converts a Flow Publisher into a Reactive Streams Publisher. * @param the element type * @param flowPublisher the source Flow Publisher to convert - * @return the equivalent Reactive Publisher + * @return the equivalent Reactive Streams Publisher */ @SuppressWarnings("unchecked") - public static org.reactivestreams.Publisher toReactive( + public static org.reactivestreams.Publisher toReactiveStreams( Flow.Publisher flowPublisher) { if (flowPublisher == null) { throw new NullPointerException("flowPublisher"); @@ -38,42 +38,42 @@ public static org.reactivestreams.Publisher toReactive( return (org.reactivestreams.Publisher)flowPublisher; } if (flowPublisher instanceof FlowPublisherFromReactive) { - return (org.reactivestreams.Publisher)(((FlowPublisherFromReactive)flowPublisher).reactive); + return (org.reactivestreams.Publisher)(((FlowPublisherFromReactive)flowPublisher).reactiveStreams); } return new ReactivePublisherFromFlow(flowPublisher); } /** - * Converts a Reactive Publisher into a Flow Publisher. + * Converts a Reactive Streams Publisher into a Flow Publisher. * @param the element type - * @param reactivePublisher the source Reactive Publisher to convert + * @param reactiveStreamsPublisher the source Reactive Streams Publisher to convert * @return the equivalent Flow Publisher */ @SuppressWarnings("unchecked") public static Flow.Publisher toFlow( - org.reactivestreams.Publisher reactivePublisher + org.reactivestreams.Publisher reactiveStreamsPublisher ) { - if (reactivePublisher == null) { + if (reactiveStreamsPublisher == null) { throw new NullPointerException("reactivePublisher"); } - if (reactivePublisher instanceof Flow.Publisher) { - return (Flow.Publisher)reactivePublisher; + if (reactiveStreamsPublisher instanceof Flow.Publisher) { + return (Flow.Publisher)reactiveStreamsPublisher; } - if (reactivePublisher instanceof ReactivePublisherFromFlow) { - return (Flow.Publisher)(((ReactivePublisherFromFlow)reactivePublisher).flow); + if (reactiveStreamsPublisher instanceof ReactivePublisherFromFlow) { + return (Flow.Publisher)(((ReactivePublisherFromFlow)reactiveStreamsPublisher).flow); } - return new FlowPublisherFromReactive(reactivePublisher); + return new FlowPublisherFromReactive(reactiveStreamsPublisher); } /** - * Converts a Flow Processor into a Reactive Processor. + * Converts a Flow Processor into a Reactive Streams Processor. * @param the input value type * @param the output value type * @param flowProcessor the source Flow Processor to convert - * @return the equivalent Reactive Processor + * @return the equivalent Reactive Streams Processor */ @SuppressWarnings("unchecked") - public static org.reactivestreams.Processor toReactive( + public static org.reactivestreams.Processor toReactiveStreams( Flow.Processor flowProcessor ) { if (flowProcessor == null) { @@ -83,58 +83,58 @@ public static org.reactivestreams.Processor toReactive( return (org.reactivestreams.Processor)flowProcessor; } if (flowProcessor instanceof FlowToReactiveProcessor) { - return (org.reactivestreams.Processor)(((FlowToReactiveProcessor)flowProcessor).reactive); + return (org.reactivestreams.Processor)(((FlowToReactiveProcessor)flowProcessor).reactiveStreams); } return new ReactiveToFlowProcessor(flowProcessor); } /** - * Converts a Reactive Processor into a Flow Processor. + * Converts a Reactive Streams Processor into a Flow Processor. * @param the input value type * @param the output value type - * @param reactiveProcessor the source Reactive Processor to convert + * @param reactiveStreamsProcessor the source Reactive Streams Processor to convert * @return the equivalent Flow Processor */ @SuppressWarnings("unchecked") public static Flow.Processor toFlow( - org.reactivestreams.Processor reactiveProcessor + org.reactivestreams.Processor reactiveStreamsProcessor ) { - if (reactiveProcessor == null) { + if (reactiveStreamsProcessor == null) { throw new NullPointerException("reactiveProcessor"); } - if (reactiveProcessor instanceof Flow.Processor) { - return (Flow.Processor)reactiveProcessor; + if (reactiveStreamsProcessor instanceof Flow.Processor) { + return (Flow.Processor)reactiveStreamsProcessor; } - if (reactiveProcessor instanceof ReactiveToFlowProcessor) { - return (Flow.Processor)(((ReactiveToFlowProcessor)reactiveProcessor).flow); + if (reactiveStreamsProcessor instanceof ReactiveToFlowProcessor) { + return (Flow.Processor)(((ReactiveToFlowProcessor)reactiveStreamsProcessor).flow); } - return new FlowToReactiveProcessor(reactiveProcessor); + return new FlowToReactiveProcessor(reactiveStreamsProcessor); } /** - * Wraps a Reactive Subscription and converts the calls to a Flow Subscription. + * Wraps a Reactive Streams Subscription and converts the calls to a Flow Subscription. */ static final class FlowToReactiveSubscription implements Flow.Subscription { - private final org.reactivestreams.Subscription reactive; + private final org.reactivestreams.Subscription reactiveStreams; public FlowToReactiveSubscription(org.reactivestreams.Subscription reactive) { - this.reactive = reactive; + this.reactiveStreams = reactive; } @Override public void request(long n) { - reactive.request(n); + reactiveStreams.request(n); } @Override public void cancel() { - reactive.cancel(); + reactiveStreams.cancel(); } } /** - * Wraps a Flow Subscription and converts the calls to a Reactive Subscription. + * Wraps a Flow Subscription and converts the calls to a Reactive Streams Subscription. */ static final class ReactiveToFlowSubscription implements org.reactivestreams.Subscription { private final Flow.Subscription flow; @@ -157,41 +157,41 @@ public void cancel() { } /** - * Wraps a Reactive Subscriber and forwards methods of the Flow Subscriber to it. + * Wraps a Reactive Streams Subscriber and forwards methods of the Flow Subscriber to it. * @param the element type */ static final class FlowToReactiveSubscriber implements Flow.Subscriber { - private final org.reactivestreams.Subscriber reactive; + private final org.reactivestreams.Subscriber reactiveStreams; public FlowToReactiveSubscriber(org.reactivestreams.Subscriber reactive) { - this.reactive = reactive; + this.reactiveStreams = reactive; } @Override public void onSubscribe(Flow.Subscription subscription) { - reactive.onSubscribe(new ReactiveToFlowSubscription(subscription)); + reactiveStreams.onSubscribe(new ReactiveToFlowSubscription(subscription)); } @Override public void onNext(T item) { - reactive.onNext(item); + reactiveStreams.onNext(item); } @Override public void onError(Throwable throwable) { - reactive.onError(throwable); + reactiveStreams.onError(throwable); } @Override public void onComplete() { - reactive.onComplete(); + reactiveStreams.onComplete(); } } /** - * Wraps a Reactive Subscriber and forwards methods of the Flow Subscriber to it. + * Wraps a Reactive Streams Subscriber and forwards methods of the Flow Subscriber to it. * @param the element type */ static final class ReactiveToFlowSubscriber @@ -225,13 +225,13 @@ public void onComplete() { } /** - * Wraps a Flow Processor and forwards methods of the Reactive Processor to it. + * Wraps a Flow Processor and forwards methods of the Reactive Streams Processor to it. * @param the input type * @param the output type */ static final class ReactiveToFlowProcessor implements org.reactivestreams.Processor { - private final Flow.Processor flow; + final Flow.Processor flow; public ReactiveToFlowProcessor(Flow.Processor flow) { this.flow = flow; @@ -268,50 +268,50 @@ public void subscribe(org.reactivestreams.Subscriber s) { } /** - * Wraps a Reactive Processor and forwards methods of the Flow Processor to it. + * Wraps a Reactive Streams Processor and forwards methods of the Flow Processor to it. * @param the input type * @param the output type */ static final class FlowToReactiveProcessor implements Flow.Processor { - private final org.reactivestreams.Processor reactive; + final org.reactivestreams.Processor reactiveStreams; public FlowToReactiveProcessor(org.reactivestreams.Processor reactive) { - this.reactive = reactive; + this.reactiveStreams = reactive; } @Override public void onSubscribe(Flow.Subscription s) { - reactive.onSubscribe(new ReactiveToFlowSubscription(s)); + reactiveStreams.onSubscribe(new ReactiveToFlowSubscription(s)); } @Override public void onNext(T t) { - reactive.onNext(t); + reactiveStreams.onNext(t); } @Override public void onError(Throwable t) { - reactive.onError(t); + reactiveStreams.onError(t); } @Override public void onComplete() { - reactive.onComplete(); + reactiveStreams.onComplete(); } @Override public void subscribe(Flow.Subscriber s) { if (s == null) { - reactive.subscribe(null); + reactiveStreams.subscribe(null); return; } - reactive.subscribe(new ReactiveToFlowSubscriber(s)); + reactiveStreams.subscribe(new ReactiveToFlowSubscriber(s)); } } /** - * Reactive Publisher that wraps a Flow Publisher. + * Reactive Streams Publisher that wraps a Flow Publisher. * @param the element type */ static final class ReactivePublisherFromFlow implements org.reactivestreams.Publisher { @@ -333,24 +333,24 @@ public void subscribe(org.reactivestreams.Subscriber reactive) { } /** - * Flow Publisher that wraps a Reactive Publisher. + * Flow Publisher that wraps a Reactive Streams Publisher. * @param the element type */ static final class FlowPublisherFromReactive implements Flow.Publisher { - final org.reactivestreams.Publisher reactive; + final org.reactivestreams.Publisher reactiveStreams; public FlowPublisherFromReactive(org.reactivestreams.Publisher reactivePublisher) { - this.reactive = reactivePublisher; + this.reactiveStreams = reactivePublisher; } @Override public void subscribe(Flow.Subscriber flow) { if (flow == null) { - reactive.subscribe(null); + reactiveStreams.subscribe(null); return; } - reactive.subscribe(new ReactiveToFlowSubscriber(flow)); + reactiveStreams.subscribe(new ReactiveToFlowSubscriber(flow)); } } diff --git a/flow-bridge/src/test/java/org/reactivestreams/ReactiveFlowBridgeTest.java b/flow-bridge/src/test/java/org/reactivestreams/ReactiveFlowBridgeTest.java index fd6675fa..43870678 100644 --- a/flow-bridge/src/test/java/org/reactivestreams/ReactiveFlowBridgeTest.java +++ b/flow-bridge/src/test/java/org/reactivestreams/ReactiveFlowBridgeTest.java @@ -76,7 +76,7 @@ public void execute(Runnable command) { TestConsumer tc = new TestConsumer(); - ReactiveFlowBridge.toReactive(p).subscribe(tc); + ReactiveFlowBridge.toReactiveStreams(p).subscribe(tc); p.submit(1); p.submit(2); @@ -99,7 +99,7 @@ public void execute(Runnable command) { TestConsumer tc = new TestConsumer(); - ReactiveFlowBridge.toReactive(p).subscribe(tc); + ReactiveFlowBridge.toReactiveStreams(p).subscribe(tc); p.submit(1); p.submit(2); diff --git a/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java b/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java index e8261d8c..dc52acd3 100644 --- a/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java +++ b/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java @@ -11,15 +11,11 @@ package org.reactivestreams; -import org.reactivestreams.Publisher; import org.reactivestreams.tck.PublisherVerification; import org.reactivestreams.tck.TestEnvironment; import org.testng.annotations.Test; import java.io.IOException; -import java.util.Objects; -import java.util.concurrent.Flow; -import java.util.concurrent.ForkJoinPool; import java.util.concurrent.SubmissionPublisher; @Test @@ -44,14 +40,14 @@ public void run() { sp.close(); } }).start(); - return ReactiveFlowBridge.toReactive(sp); + return ReactiveFlowBridge.toReactiveStreams(sp); } @Override public Publisher createFailedPublisher() { final SubmissionPublisher sp = new SubmissionPublisher(); sp.closeExceptionally(new IOException()); - return ReactiveFlowBridge.toReactive(sp); + return ReactiveFlowBridge.toReactiveStreams(sp); } @Override From 2f9c9cf1085aced8d97387d0240154be9833cd18 Mon Sep 17 00:00:00 2001 From: akarnokd Date: Fri, 22 Sep 2017 14:43:38 +0200 Subject: [PATCH 5/6] NPE to use the updated parameter name. --- .../src/main/java/org/reactivestreams/ReactiveFlowBridge.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java b/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java index 27494d95..b3461988 100644 --- a/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java +++ b/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java @@ -54,7 +54,7 @@ public static Flow.Publisher toFlow( org.reactivestreams.Publisher reactiveStreamsPublisher ) { if (reactiveStreamsPublisher == null) { - throw new NullPointerException("reactivePublisher"); + throw new NullPointerException("reactiveStreamsPublisher"); } if (reactiveStreamsPublisher instanceof Flow.Publisher) { return (Flow.Publisher)reactiveStreamsPublisher; @@ -100,7 +100,7 @@ public static Flow.Processor toFlow( org.reactivestreams.Processor reactiveStreamsProcessor ) { if (reactiveStreamsProcessor == null) { - throw new NullPointerException("reactiveProcessor"); + throw new NullPointerException("reactiveStreamsProcessor"); } if (reactiveStreamsProcessor instanceof Flow.Processor) { return (Flow.Processor)reactiveStreamsProcessor; From 4239382d66b7a9c875c675c03854e09444130a02 Mon Sep 17 00:00:00 2001 From: akarnokd Date: Mon, 25 Sep 2017 13:09:30 +0200 Subject: [PATCH 6/6] Rename bridge class, tester class (+javadoc) --- ...ge.java => ReactiveStreamsFlowBridge.java} | 4 +-- ...ava => ReactiveStreamsFlowBridgeTest.java} | 18 ++++++------- .../SubmissionPublisherTckTest.java | 4 +-- ...tConsumer.java => TestEitherConsumer.java} | 25 +++++++++++++------ 4 files changed, 31 insertions(+), 20 deletions(-) rename flow-bridge/src/main/java/org/reactivestreams/{ReactiveFlowBridge.java => ReactiveStreamsFlowBridge.java} (99%) rename flow-bridge/src/test/java/org/reactivestreams/{ReactiveFlowBridgeTest.java => ReactiveStreamsFlowBridgeTest.java} (82%) rename flow-bridge/src/test/java/org/reactivestreams/{TestConsumer.java => TestEitherConsumer.java} (85%) diff --git a/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java b/flow-bridge/src/main/java/org/reactivestreams/ReactiveStreamsFlowBridge.java similarity index 99% rename from flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java rename to flow-bridge/src/main/java/org/reactivestreams/ReactiveStreamsFlowBridge.java index b3461988..a6914df8 100644 --- a/flow-bridge/src/main/java/org/reactivestreams/ReactiveFlowBridge.java +++ b/flow-bridge/src/main/java/org/reactivestreams/ReactiveStreamsFlowBridge.java @@ -16,9 +16,9 @@ /** * Bridge between Reactive Streams API and the Java 9 {@link java.util.concurrent.Flow} API. */ -public final class ReactiveFlowBridge { +public final class ReactiveStreamsFlowBridge { /** Utility class. */ - private ReactiveFlowBridge() { + private ReactiveStreamsFlowBridge() { throw new IllegalStateException("No instances!"); } diff --git a/flow-bridge/src/test/java/org/reactivestreams/ReactiveFlowBridgeTest.java b/flow-bridge/src/test/java/org/reactivestreams/ReactiveStreamsFlowBridgeTest.java similarity index 82% rename from flow-bridge/src/test/java/org/reactivestreams/ReactiveFlowBridgeTest.java rename to flow-bridge/src/test/java/org/reactivestreams/ReactiveStreamsFlowBridgeTest.java index 43870678..e1c76087 100644 --- a/flow-bridge/src/test/java/org/reactivestreams/ReactiveFlowBridgeTest.java +++ b/flow-bridge/src/test/java/org/reactivestreams/ReactiveStreamsFlowBridgeTest.java @@ -18,7 +18,7 @@ import java.util.concurrent.Flow; import java.util.concurrent.SubmissionPublisher; -public class ReactiveFlowBridgeTest { +public class ReactiveStreamsFlowBridgeTest { @Test public void reactiveToFlowNormal() { MulticastPublisher p = new MulticastPublisher(new Executor() { @@ -28,9 +28,9 @@ public void execute(Runnable command) { } }, Flow.defaultBufferSize()); - TestConsumer tc = new TestConsumer(); + TestEitherConsumer tc = new TestEitherConsumer(); - ReactiveFlowBridge.toFlow(p).subscribe(tc); + ReactiveStreamsFlowBridge.toFlow(p).subscribe(tc); p.offer(1); p.offer(2); @@ -51,9 +51,9 @@ public void execute(Runnable command) { } }, Flow.defaultBufferSize()); - TestConsumer tc = new TestConsumer(); + TestEitherConsumer tc = new TestEitherConsumer(); - ReactiveFlowBridge.toFlow(p).subscribe(tc); + ReactiveStreamsFlowBridge.toFlow(p).subscribe(tc); p.offer(1); p.offer(2); @@ -74,9 +74,9 @@ public void execute(Runnable command) { } }, Flow.defaultBufferSize()); - TestConsumer tc = new TestConsumer(); + TestEitherConsumer tc = new TestEitherConsumer(); - ReactiveFlowBridge.toReactiveStreams(p).subscribe(tc); + ReactiveStreamsFlowBridge.toReactiveStreams(p).subscribe(tc); p.submit(1); p.submit(2); @@ -97,9 +97,9 @@ public void execute(Runnable command) { } }, Flow.defaultBufferSize()); - TestConsumer tc = new TestConsumer(); + TestEitherConsumer tc = new TestEitherConsumer(); - ReactiveFlowBridge.toReactiveStreams(p).subscribe(tc); + ReactiveStreamsFlowBridge.toReactiveStreams(p).subscribe(tc); p.submit(1); p.submit(2); diff --git a/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java b/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java index dc52acd3..cc342924 100644 --- a/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java +++ b/flow-bridge/src/test/java/org/reactivestreams/SubmissionPublisherTckTest.java @@ -40,14 +40,14 @@ public void run() { sp.close(); } }).start(); - return ReactiveFlowBridge.toReactiveStreams(sp); + return ReactiveStreamsFlowBridge.toReactiveStreams(sp); } @Override public Publisher createFailedPublisher() { final SubmissionPublisher sp = new SubmissionPublisher(); sp.closeExceptionally(new IOException()); - return ReactiveFlowBridge.toReactiveStreams(sp); + return ReactiveStreamsFlowBridge.toReactiveStreams(sp); } @Override diff --git a/flow-bridge/src/test/java/org/reactivestreams/TestConsumer.java b/flow-bridge/src/test/java/org/reactivestreams/TestEitherConsumer.java similarity index 85% rename from flow-bridge/src/test/java/org/reactivestreams/TestConsumer.java rename to flow-bridge/src/test/java/org/reactivestreams/TestEitherConsumer.java index 7ee479cc..5b8d8e63 100644 --- a/flow-bridge/src/test/java/org/reactivestreams/TestConsumer.java +++ b/flow-bridge/src/test/java/org/reactivestreams/TestEitherConsumer.java @@ -18,7 +18,18 @@ import java.util.concurrent.Flow; import java.util.concurrent.TimeUnit; -class TestConsumer implements Flow.Subscriber, Subscriber { +/** + * Class that provides basic state assertions on received elements and + * terminal signals from either a Reactive Streams Publisher or a + * Flow Publisher. + *

+ * As with standard {@link Subscriber}s, an instance of this class + * should be subscribed to at most one (Reactive Streams or + * Flow) Publisher. + *

+ * @param the element type + */ +class TestEitherConsumer implements Flow.Subscriber, Subscriber { protected final List values; @@ -34,11 +45,11 @@ class TestConsumer implements Flow.Subscriber, Subscriber { final long initialRequest; - public TestConsumer() { + public TestEitherConsumer() { this(Long.MAX_VALUE); } - public TestConsumer(long initialRequest) { + public TestEitherConsumer(long initialRequest) { this.values = new ArrayList(); this.errors = new ArrayList(); this.done = new CountDownLatch(1); @@ -103,7 +114,7 @@ public final boolean await(long timeout, TimeUnit unit) throws InterruptedExcept return done.await(timeout, unit); } - public final TestConsumer assertResult(T... items) { + public final TestEitherConsumer assertResult(T... items) { if (!values.equals(Arrays.asList(items))) { throw new AssertionError("Expected: " + Arrays.toString(items) + ", Actual: " + values + ", Completions: " + completions); } @@ -114,7 +125,7 @@ public final TestConsumer assertResult(T... items) { } - public final TestConsumer assertFailure(Class errorClass, T... items) { + public final TestEitherConsumer assertFailure(Class errorClass, T... items) { if (!values.equals(Arrays.asList(items))) { throw new AssertionError("Expected: " + Arrays.toString(items) + ", Actual: " + values + ", Completions: " + completions); } @@ -132,7 +143,7 @@ public final TestConsumer assertFailure(Class errorClass return this; } - public final TestConsumer awaitDone(long timeout, TimeUnit unit) { + public final TestEitherConsumer awaitDone(long timeout, TimeUnit unit) { try { if (!done.await(timeout, unit)) { subscription.cancel(); @@ -145,7 +156,7 @@ public final TestConsumer awaitDone(long timeout, TimeUnit unit) { return this; } - public final TestConsumer assertRange(int start, int count) { + public final TestEitherConsumer assertRange(int start, int count) { if (values.size() != count) { throw new AssertionError("Expected: " + count + ", Actual: " + values.size()); }