17
17
package org .springframework .integration .channel ;
18
18
19
19
import java .time .Duration ;
20
+ import java .util .ArrayList ;
21
+ import java .util .List ;
20
22
import java .util .concurrent .TimeUnit ;
21
23
import java .util .concurrent .atomic .AtomicReference ;
22
24
import java .util .concurrent .locks .LockSupport ;
30
32
import reactor .core .publisher .Sinks ;
31
33
import reactor .util .context .ContextView ;
32
34
35
+ import org .springframework .context .Lifecycle ;
33
36
import org .springframework .core .log .LogMessage ;
34
37
import org .springframework .integration .IntegrationMessageHeaderAccessor ;
35
38
import org .springframework .integration .StaticMessageHeaderAccessor ;
38
41
import org .springframework .messaging .Message ;
39
42
import org .springframework .messaging .MessageDeliveryException ;
40
43
import org .springframework .util .Assert ;
44
+ import org .springframework .util .ReflectionUtils ;
41
45
42
46
/**
43
47
* The {@link AbstractMessageChannel} implementation for the
44
48
* Reactive Streams {@link Publisher} based on the Project Reactor {@link Flux}.
49
+ * <p>
50
+ * This class implements {@link Lifecycle} to control subscriptions to publishers
51
+ * attached via {@link #subscribeTo(Publisher)}, when this channel is restarted.
45
52
*
46
53
* @author Artem Bilan
47
54
* @author Gary Russell
50
57
* @since 5.0
51
58
*/
52
59
public class FluxMessageChannel extends AbstractMessageChannel
53
- implements Publisher <Message <?>>, ReactiveStreamsSubscribableChannel {
60
+ implements Publisher <Message <?>>, ReactiveStreamsSubscribableChannel , Lifecycle {
54
61
55
62
private final Sinks .Many <Message <?>> sink = Sinks .many ().multicast ().onBackpressureBuffer (1 , false );
56
63
57
- private final Disposable .Composite upstreamSubscriptions = Disposables .composite ();
64
+ private final List <Publisher <? extends Message <?>>> sourcePublishers = new ArrayList <>();
65
+
66
+ private volatile Disposable .Composite upstreamSubscriptions = Disposables .composite ();
58
67
59
68
private volatile boolean active = true ;
60
69
@@ -111,19 +120,22 @@ public void subscribe(Subscriber<? super Message<?>> subscriber) {
111
120
.subscribe (subscriber );
112
121
}
113
122
114
- private void addPublisherToSubscribe (Flux <?> publisher ) {
115
- AtomicReference <Disposable > disposableReference = new AtomicReference <>();
123
+ @ Override
124
+ public void start () {
125
+ this .active = true ;
126
+ this .upstreamSubscriptions = Disposables .composite ();
127
+ this .sourcePublishers .forEach (this ::doSubscribeTo );
128
+ }
116
129
117
- Disposable disposable =
118
- publisher
119
- .doOnTerminate (() -> disposeUpstreamSubscription (disposableReference ))
120
- .subscribe ();
130
+ @ Override
131
+ public void stop () {
132
+ this .active = false ;
133
+ this .upstreamSubscriptions .dispose ();
134
+ }
121
135
122
- if (!disposable .isDisposed ()) {
123
- if (this .upstreamSubscriptions .add (disposable )) {
124
- disposableReference .set (disposable );
125
- }
126
- }
136
+ @ Override
137
+ public boolean isRunning () {
138
+ return this .active ;
127
139
}
128
140
129
141
private void disposeUpstreamSubscription (AtomicReference <Disposable > disposableReference ) {
@@ -136,8 +148,14 @@ private void disposeUpstreamSubscription(AtomicReference<Disposable> disposableR
136
148
137
149
@ Override
138
150
public void subscribeTo (Publisher <? extends Message <?>> publisher ) {
151
+ this .sourcePublishers .add (publisher );
152
+ doSubscribeTo (publisher );
153
+ }
154
+
155
+ private void doSubscribeTo (Publisher <? extends Message <?>> publisher ) {
139
156
Flux <Object > upstreamPublisher =
140
157
Flux .from (publisher )
158
+ .doOnComplete (() -> this .sourcePublishers .remove (publisher ))
141
159
.delaySubscription (
142
160
Mono .fromCallable (this .sink ::currentSubscriberCount )
143
161
.filter ((value ) -> value > 0 )
@@ -152,6 +170,21 @@ public void subscribeTo(Publisher<? extends Message<?>> publisher) {
152
170
addPublisherToSubscribe (upstreamPublisher );
153
171
}
154
172
173
+ private void addPublisherToSubscribe (Flux <?> publisher ) {
174
+ AtomicReference <Disposable > disposableReference = new AtomicReference <>();
175
+
176
+ Disposable disposable =
177
+ publisher
178
+ .doOnTerminate (() -> disposeUpstreamSubscription (disposableReference ))
179
+ .subscribe ();
180
+
181
+ if (!disposable .isDisposed ()) {
182
+ if (this .upstreamSubscriptions .add (disposable )) {
183
+ disposableReference .set (disposable );
184
+ }
185
+ }
186
+ }
187
+
155
188
private void sendReactiveMessage (Message <?> message ) {
156
189
Message <?> messageToSend = message ;
157
190
// We have just restored Reactor context, so no need in a header anymore.
@@ -169,14 +202,20 @@ private void sendReactiveMessage(Message<?> message) {
169
202
}
170
203
}
171
204
catch (Exception ex ) {
172
- logger .warn (ex , LogMessage .format ("Error during processing event: %s" , messageToSend ));
205
+ if (isApplicationRunning ()) {
206
+ logger .error (ex , LogMessage .format ("Error during processing event: %s" , messageToSend ));
207
+ }
208
+ else {
209
+ ReflectionUtils .rethrowRuntimeException (ex );
210
+ }
173
211
}
174
212
}
175
213
176
214
@ Override
177
215
public void destroy () {
178
216
this .active = false ;
179
217
this .upstreamSubscriptions .dispose ();
218
+ this .sourcePublishers .clear ();
180
219
this .sink .emitComplete (Sinks .EmitFailureHandler .busyLooping (Duration .ofSeconds (1 )));
181
220
super .destroy ();
182
221
}
0 commit comments