@Override public void connect(Consumer<? super Disposable> cancelSupport) { this.source.connect(); }
/** * Connect this {@link ConnectableFlux} to its source and return a {@link Runnable} that * can be used for disconnecting. * @return the {@link Disposable} that allows disconnecting the connection after. */ public final Disposable connect() { final Disposable[] out = { null }; connect(r -> out[0] = r); return out[0]; }
@Override public void connect(Consumer<? super Disposable> cancelSupport) { source.connect(cancelSupport); }
@Override public void connect(Consumer<? super Disposable> cancelSupport) { this.source.connect(); }
@Override public void subscribe(CoreSubscriber<? super T> actual) { source.subscribe(actual); if (remaining > 0 && REMAINING.decrementAndGet(this) == 0) { source.connect(cancelSupport); } }
@Override public void subscribe(CoreSubscriber<? super T> actual) { source.subscribe(actual); if (remaining > 0 && REMAINING.decrementAndGet(this) == 0) { source.connect(cancelSupport); } }
@Override public void subscribe(CoreSubscriber<? super T> actual) { RefCountMonitor<T> conn; boolean connect = false; synchronized (this) { conn = connection; if (conn == null || conn.terminated) { conn = new RefCountMonitor<>(this); connection = conn; } long c = conn.subscribers; conn.subscribers = c + 1; if (!conn.connected && c + 1 == n) { connect = true; conn.connected = true; } } source.subscribe(new RefCountInner<>(actual, conn)); if (connect) { source.connect(conn); } }
/** * Connects this {@link ConnectableFlux} to the upstream source when the specified amount of * {@link org.reactivestreams.Subscriber} subscribes and calls the supplied consumer with a runnable that allows disconnecting. * <p> * <img class="marble" src="doc-files/marbles/autoConnectWithMinSubscribers.svg" alt=""> * * @param minSubscribers the minimum number of subscribers * @param cancelSupport the consumer that will receive the {@link Disposable} that allows disconnecting * @return a {@link Flux} that connects to the upstream source when the given amount of subscribers subscribed */ public final Flux<T> autoConnect(int minSubscribers, Consumer<? super Disposable> cancelSupport) { if (minSubscribers == 0) { connect(cancelSupport); return this; } if(this instanceof Fuseable){ return onAssembly(new FluxAutoConnectFuseable<>(this, minSubscribers, cancelSupport)); } return onAssembly(new FluxAutoConnect<>(this, minSubscribers, cancelSupport)); }
@Test public void advancedConnectable() throws InterruptedException { Flux<Integer> source = Flux.range(1, 3) .doOnSubscribe(s -> System.out.println("subscribed to source")); ConnectableFlux<Integer> co = source.publish(); co.subscribe(System.out::println, e -> {}, () -> {}); co.subscribe(System.out::println, e -> {}, () -> {}); System.out.println("done subscribing"); Thread.sleep(500); System.out.println("will now connect"); co.connect(); }
@Override public void subscribe(CoreSubscriber<? super T> actual) { RefConnection conn; boolean connect = false; synchronized (this) { conn = connection; if (conn == null || conn.terminated) { conn = new RefConnection(this); connection = conn; } long c = conn.subscriberCount; if (c == 0L && conn.timer != null) { conn.timer.dispose(); } conn.subscriberCount = c + 1; if (!conn.connected && c + 1 == n) { connect = true; conn.connected = true; } } source.subscribe(new RefCountInner<>(actual, this, conn)); if (connect) { source.connect(conn); } }
@Test public void cancel() { ConnectableFlux<Integer> replay = UnicastProcessor.<Integer>create() .replay(2); replay.subscribe(v -> {}, e -> { throw Exceptions.propagate(e); }); Disposable connected = replay.connect(); //the lambda subscriber itself is cancelled so it will bubble the exception //propagated by connect().dispose() assertThatExceptionOfType(RuntimeException.class) .isThrownBy(connected::dispose) .withMessage("Disconnected"); boolean cancelled = ((FluxReplay.ReplaySubscriber) connected).cancelled; assertThat(cancelled).isTrue(); }
@Test public void fusedMapInvalid() { AssertSubscriber<Integer> ts = AssertSubscriber.create(); ConnectableFlux<Integer> p = Flux.range(1, 5).map(v -> (Integer)null).publish(); p.subscribe(ts); p.connect(); ts.assertNoValues() .assertError(NullPointerException.class) .assertNotComplete(); }
@Test public void error() { AssertSubscriber<Integer> ts = AssertSubscriber.create(); EmitterProcessor<Integer> e = EmitterProcessor.create(); ConnectableFlux<Integer> p = e.publish(); p.subscribe(ts); p.connect(); e.onNext(1); e.onNext(2); e.onError(new RuntimeException("forced failure")); ts.assertValues(1, 2) .assertError(RuntimeException.class) .assertErrorWith( x -> Assert.assertTrue(x.getMessage().contains("forced failure"))) .assertNotComplete(); }
@Test public void testMultiReceiver() throws Exception { Hooks.onOperatorDebug(); try { ConnectableFlux<?> t = Flux.empty() .then(Mono.defer(() -> { throw new RuntimeException(); })).flux().publish(); t.map(d -> d).subscribe(null, e -> Assert.assertTrue(e.getSuppressed()[0].getMessage().contains ("\t|_\tFlux.publish"))); t.filter(d -> true).subscribe(null, e -> Assert.assertTrue(e.getSuppressed()[0].getMessage().contains ("\t\t|_\tFlux.publish"))); t.distinct().subscribe(null, e -> Assert.assertTrue(e.getSuppressed()[0].getMessage().contains ("\t\t\t|_\tFlux.publish"))); t.connect(); } finally { Hooks.resetOnOperatorDebug(); } }
@Test public void testMultiReceiver() throws Exception { Hooks.onOperatorDebug(); try { ConnectableFlux<?> t = Flux.empty() .then(Mono.defer(() -> { throw new RuntimeException(); })).flux().publish(); t.map(d -> d).subscribe(null, e -> assertThat(e.getSuppressed()[0]).hasMessageContaining("\t|_\tFlux.publish")); t.filter(d -> true).subscribe(null, e -> assertThat(e.getSuppressed()[0]).hasMessageContaining("\t\t|_\tFlux.publish")); t.distinct().subscribe(null, e -> assertThat(e.getSuppressed()[0]).hasMessageContaining("\t\t\t|_\tFlux.publish")); t.connect(); } finally { Hooks.resetOnOperatorDebug(); } }
@Test public void disconnectBackpressured() { AssertSubscriber<Integer> ts = AssertSubscriber.create(0); EmitterProcessor<Integer> e = EmitterProcessor.create(); ConnectableFlux<Integer> p = e.publish(); p.subscribe(ts); Disposable r = p.connect(); r.dispose(); ts.assertNoValues() .assertError(CancellationException.class) .assertNotComplete(); Assert.assertFalse("sp has subscribers?", e.downstreamCount() != 0); }
@Test public void disconnect() { AssertSubscriber<Integer> ts = AssertSubscriber.create(); EmitterProcessor<Integer> e = EmitterProcessor.create(); ConnectableFlux<Integer> p = e.publish(); p.subscribe(ts); Disposable r = p.connect(); e.onNext(1); e.onNext(2); r.dispose(); ts.assertValues(1, 2) .assertError(CancellationException.class) .assertNotComplete(); Assert.assertFalse("sp has subscribers?", e.downstreamCount() != 0); }
@Test public void normalSyncFused() { AssertSubscriber<Integer> ts1 = AssertSubscriber.create(); AssertSubscriber<Integer> ts2 = AssertSubscriber.create(); ConnectableFlux<Integer> p = Flux.range(1, 5).publish(5); p.subscribe(ts1); p.subscribe(ts2); ts1 .assertNoValues() .assertNoError() .assertNotComplete(); ts2 .assertNoValues() .assertNoError() .assertNotComplete(); p.connect(); ts1.assertValues(1, 2, 3, 4, 5) .assertNoError() .assertComplete(); ts2.assertValues(1, 2, 3, 4, 5) .assertNoError() .assertComplete(); }
@Test public void normalAsyncFused() { AssertSubscriber<Integer> ts1 = AssertSubscriber.create(); AssertSubscriber<Integer> ts2 = AssertSubscriber.create(); UnicastProcessor<Integer> up = UnicastProcessor.create(Queues.<Integer>get(8).get()); up.onNext(1); up.onNext(2); up.onNext(3); up.onNext(4); up.onNext(5); up.onComplete(); ConnectableFlux<Integer> p = up.publish(); p.subscribe(ts1); p.subscribe(ts2); ts1 .assertNoValues() .assertNoError() .assertNotComplete(); ts2 .assertNoValues() .assertNoError() .assertNotComplete(); p.connect(); ts1.assertValues(1, 2, 3, 4, 5) .assertNoError() .assertComplete(); ts2.assertValues(1, 2, 3, 4, 5) .assertNoError() .assertComplete(); }
@Test public void normal() { AssertSubscriber<Integer> ts1 = AssertSubscriber.create(); AssertSubscriber<Integer> ts2 = AssertSubscriber.create(); ConnectableFlux<Integer> p = Flux.range(1, 5).hide().publish(); p.subscribe(ts1); p.subscribe(ts2); ts1 .assertNoValues() .assertNoError() .assertNotComplete(); ts2 .assertNoValues() .assertNoError() .assertNotComplete(); p.connect(); ts1.assertValues(1, 2, 3, 4, 5) .assertNoError() .assertComplete(); ts2.assertValues(1, 2, 3, 4, 5) .assertNoError() .assertComplete(); }