You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+6-3
Original file line number
Diff line number
Diff line change
@@ -57,9 +57,12 @@ A *Publisher* is a provider of a potentially unbounded number of sequenced eleme
57
57
In response to a call to `Publisher.subscribe(Subscriber)` the possible invocation sequences for methods on the `Subscriber` are given by the following protocol:
This means that `onSubscribe` is always signalled,
64
+
followed by a possibly unbounded number of `onNext` signals (as requested by `Subscriber`) followed by an `onError` signal if there is a failure, or an `onComplete` signal when no more elements are available—all as long as the `Subscription` is not cancelled.
65
+
63
66
#### NOTES
64
67
65
68
- The specifications below use binding words in capital letters from https://www.ietf.org/rfc/rfc2119.txt
@@ -87,7 +90,7 @@ public interface Publisher<T> {
87
90
|<a name="1.6">6</a>|If a `Publisher` signals either `onError` or `onComplete` on a `Subscriber`, that `Subscriber`’s `Subscription` MUST be considered cancelled. |
88
91
|<a name="1.7">7</a>|Once a terminal state has been signaled (`onError`, `onComplete`) it is REQUIRED that no further signals occur. |
89
92
|<a name="1.8">8</a>|If a `Subscription` is cancelled its `Subscriber` MUST eventually stop being signaled. |
90
-
|<a name="1.9">9</a>|Calling`Publisher.subscribe` MUSTreturn normally except when the provided `Subscriber` is `null` in which case it MUSTthrow a `java.lang.NullPointerException` to the caller, for all other situations the only legal way to signal failure (or reject a `Subscriber`) is via the `onError` method. |
93
+
|<a name="1.9">9</a>| `Publisher.subscribe` MUSTcall `onSubscribe` on the provided `Subscriber` prior to any other signals to that `Subscriber` and MUSTreturn normally, except when the provided `Subscriber` is `null` in which case it MUSTthrow a `java.lang.NullPointerException` to the caller, for all other situations the only legal way to signal failure (or reject the `Subscriber`) is by calling `onError` (after calling `onSubscribe`).|
91
94
|<a name="1.10">10</a>| `Publisher.subscribe` MAY be called as many times as wanted but MUST be with a different `Subscriber` each time [see [2.12](#2.12)].|
92
95
|<a name="1.11">11</a>|A `Publisher` MAY support multiple `Subscriber`s and decides whether each `Subscription` is unicast or multicast. |
93
96
|<a name="1.12">12</a>|A `Publisher` MUST produce the same elements, starting with the oldest element still available, in the same sequence for all its subscribers and MAY produce the stream elements at (temporarily) differing rates to different subscribers. |
@@ -147,7 +150,7 @@ public interface Subscription {
147
150
| <a name="3.11">11</a> | While the `Subscription` is not cancelled, `Subscription.request(long n)` MAY synchronously call `onComplete` or `onError` on this (or other) subscriber(s). |
148
151
| <a name="3.12">12</a> | While the `Subscription` is not cancelled, `Subscription.cancel()` MUST request the `Publisher` to eventually stop signaling its `Subscriber`. The operation is NOT REQUIRED to affect the `Subscription` immediately. |
149
152
| <a name="3.13">13</a> | While the `Subscription` is not cancelled, `Subscription.cancel()` MUST request the `Publisher` to eventually drop any references to the corresponding subscriber. Re-subscribing with the same `Subscriber` object is discouraged [see [2.12](#2.12)], but this specification does not mandate that it is disallowed since that would mean having to store previously cancelled subscriptions indefinitely. |
150
-
| <a name="3.14">14</a> | While the `Subscription` is not cancelled, calling `Subscription.cancel` MAY cause the `Publisher`, if stateful, to transition into the `shut-down` state if no other `Subscription` exists at this point [see [1.12](#1.12)].
153
+
| <a name="3.14">14</a> | While the `Subscription` is not cancelled, calling `Subscription.cancel` MAY cause the `Publisher`, if stateful, to transition into the `shut-down` state if no other `Subscription` exists at this point [see [1.9](#1.9)].
151
154
| <a name="3.15">15</a> | Calling `Subscription.cancel` MUST return normally. The only legal way to signal failure to a `Subscriber` is via the `onError` method. |
152
155
| <a name="3.16">16</a> | Calling `Subscription.request` MUST return normally. The only legal way to signal failure to a `Subscriber` is via the `onError` method. |
153
156
| <a name="3.17">17</a> | A `Subscription` MUST support an unbounded number of calls to request and MUST support a demand (sum requested - sum delivered) up to 2^63-1 (`java.lang.Long.MAX_VALUE`). A demand equal or greater than 2^63-1 (`java.lang.Long.MAX_VALUE`) MAY be considered by the `Publisher` as “effectively unbounded”[[1](#footnote-3-1)]. |
cancelled = true; // When we signal onError, the subscription must be considered as cancelled, as per rule 1.6
178
182
try {
179
183
subscriber.onError(t); // Then we signal the error downstream, to the `Subscriber`
180
-
} catch(finalThrowablet2) { // If `onError` throws an exception, this is a spec violation according to rule 1.12, and all we can do is to log it.
184
+
} catch(finalThrowablet2) { // If `onError` throws an exception, this is a spec violation according to rule 1.9, and all we can do is to log it.
181
185
(newIllegalStateException(subscriber + " violated the Reactive Streams rule 2.13 by throwing an exception from onError.", t2)).printStackTrace(System.err);
if(subscription == null) { // Check for spec violation of 2.1
105
-
(newIllegalStateException("Someone violated the Reactive Streams rule 2.1 by signalling OnNext before `Subscription.request`. (no Subscription)")).printStackTrace(System.err);
104
+
if(subscription == null) { // Technically this check is not needed, since we are expecting Publishers to conform to the spec
105
+
// Check for spec violation of 2.1 and 1.09
106
+
(newIllegalStateException("Someone violated the Reactive Streams rule 1.09 and 2.1 by signalling OnNext before `Subscription.request`. (no Subscription)")).printStackTrace(System.err);
106
107
} else {
107
108
try {
108
109
if (whenNext(element)) {
@@ -116,7 +117,7 @@ private final void handleOnNext(final T element) {
116
117
done(); // This is legal according to rule 2.6
117
118
}
118
119
} catch(finalThrowablet) {
119
-
done();
120
+
done();
120
121
try {
121
122
onError(t);
122
123
} catch(finalThrowablet2) {
@@ -130,21 +131,32 @@ private final void handleOnNext(final T element) {
130
131
131
132
// Here it is important that we do not violate 2.2 and 2.3 by calling methods on the `Subscription` or `Publisher`
132
133
privatevoidhandleOnComplete() {
133
-
done = true; // Obey rule 2.4
134
-
whenComplete();
134
+
if (subscription == null) { // Technically this check is not needed, since we are expecting Publishers to conform to the spec
135
+
// Publisher is not allowed to signal onComplete before onSubscribe according to rule 1.09
136
+
(newIllegalStateException("Publisher violated the Reactive Streams rule 1.09 signalling onComplete prior to onSubscribe.")).printStackTrace(System.err);
137
+
} else {
138
+
done = true; // Obey rule 2.4
139
+
whenComplete();
140
+
}
135
141
}
136
142
137
143
// Here it is important that we do not violate 2.2 and 2.3 by calling methods on the `Subscription` or `Publisher`
138
144
privatevoidhandleOnError(finalThrowableerror) {
139
-
done = true; // Obey rule 2.4
140
-
whenError(error);
145
+
if (subscription == null) { // Technically this check is not needed, since we are expecting Publishers to conform to the spec
146
+
// Publisher is not allowed to signal onError before onSubscribe according to rule 1.09
147
+
(newIllegalStateException("Publisher violated the Reactive Streams rule 1.09 signalling onError prior to onSubscribe.")).printStackTrace(System.err);
148
+
} else {
149
+
done = true; // Obey rule 2.4
150
+
whenError(error);
151
+
}
141
152
}
142
153
143
154
// We implement the OnX methods on `Subscriber` to send Signals that we will process asycnhronously, but only one at a time
Copy file name to clipboardExpand all lines: examples/src/main/java/org/reactivestreams/example/unicast/SyncSubscriber.java
+37-25
Original file line number
Diff line number
Diff line change
@@ -41,28 +41,32 @@ public abstract class SyncSubscriber<T> implements Subscriber<T> {
41
41
}
42
42
43
43
@OverridepublicvoidonNext(finalTelement) {
44
-
// As per rule 2.13, we need to throw a `java.lang.NullPointerException` if the `element` is `null`
45
-
if (element == null) thrownull;
44
+
if (subscription == null) { // Technically this check is not needed, since we are expecting Publishers to conform to the spec
45
+
(newIllegalStateException("Publisher violated the Reactive Streams rule 1.09 signalling onNext prior to onSubscribe.")).printStackTrace(System.err);
46
+
} else {
47
+
// As per rule 2.13, we need to throw a `java.lang.NullPointerException` if the `element` is `null`
48
+
if (element == null) thrownull;
46
49
47
-
if (!done) { // If we aren't already done
48
-
try {
49
-
if (foreach(element)) {
50
-
try {
51
-
subscription.request(1); // Our Subscriber is unbuffered and modest, it requests one element at a time
52
-
} catch(finalThrowablet) {
53
-
// Subscription.request is not allowed to throw according to rule 3.16
54
-
(newIllegalStateException(subscription + " violated the Reactive Streams rule 3.16 by throwing an exception from cancel.", t)).printStackTrace(System.err);
50
+
if (!done) { // If we aren't already done
51
+
try {
52
+
if (foreach(element)) {
53
+
try {
54
+
subscription.request(1); // Our Subscriber is unbuffered and modest, it requests one element at a time
55
+
} catch (finalThrowablet) {
56
+
// Subscription.request is not allowed to throw according to rule 3.16
57
+
(newIllegalStateException(subscription + " violated the Reactive Streams rule 3.16 by throwing an exception from cancel.", t)).printStackTrace(System.err);
58
+
}
59
+
} else {
60
+
done();
55
61
}
56
-
} else {
62
+
} catch (finalThrowablet) {
57
63
done();
58
-
}
59
-
} catch(finalThrowablet) {
60
-
done();
61
-
try {
62
-
onError(t);
63
-
} catch(finalThrowablet2) {
64
-
//Subscriber.onError is not allowed to throw an exception, according to rule 2.13
65
-
(newIllegalStateException(this + " violated the Reactive Streams rule 2.13 by throwing an exception from onError.", t2)).printStackTrace(System.err);
64
+
try {
65
+
onError(t);
66
+
} catch (finalThrowablet2) {
67
+
//Subscriber.onError is not allowed to throw an exception, according to rule 2.13
68
+
(newIllegalStateException(this + " violated the Reactive Streams rule 2.13 by throwing an exception from onError.", t2)).printStackTrace(System.err);
69
+
}
66
70
}
67
71
}
68
72
}
@@ -86,14 +90,22 @@ private void done() {
86
90
protectedabstractbooleanforeach(finalTelement);
87
91
88
92
@OverridepublicvoidonError(finalThrowablet) {
89
-
// As per rule 2.13, we need to throw a `java.lang.NullPointerException` if the `Throwable` is `null`
90
-
if (t == null) thrownull;
91
-
// Here we are not allowed to call any methods on the `Subscription` or the `Publisher`, as per rule 2.3
92
-
// And anyway, the `Subscription` is considered to be cancelled if this method gets called, as per rule 2.4
93
+
if (subscription == null) { // Technically this check is not needed, since we are expecting Publishers to conform to the spec
94
+
(newIllegalStateException("Publisher violated the Reactive Streams rule 1.09 signalling onError prior to onSubscribe.")).printStackTrace(System.err);
95
+
} else {
96
+
// As per rule 2.13, we need to throw a `java.lang.NullPointerException` if the `Throwable` is `null`
97
+
if (t == null) thrownull;
98
+
// Here we are not allowed to call any methods on the `Subscription` or the `Publisher`, as per rule 2.3
99
+
// And anyway, the `Subscription` is considered to be cancelled if this method gets called, as per rule 2.4
100
+
}
93
101
}
94
102
95
103
@OverridepublicvoidonComplete() {
96
-
// Here we are not allowed to call any methods on the `Subscription` or the `Publisher`, as per rule 2.3
97
-
// And anyway, the `Subscription` is considered to be cancelled if this method gets called, as per rule 2.4
104
+
if (subscription == null) { // Technically this check is not needed, since we are expecting Publishers to conform to the spec
105
+
(newIllegalStateException("Publisher violated the Reactive Streams rule 1.09 signalling onComplete prior to onSubscribe.")).printStackTrace(System.err);
106
+
} else {
107
+
// Here we are not allowed to call any methods on the `Subscription` or the `Publisher`, as per rule 2.3
108
+
// And anyway, the `Subscription` is considered to be cancelled if this method gets called, as per rule 2.4
0 commit comments