I have a reactor.core.publisher.Mono variable and want to subscribe to org.reactivestreams.Subscriber, though it seems not works. I cannot get Why onNext method never called? I see onSubscribe method called fine. I could be mistaken, but as Mono implements Publisher, subscriber should work. Right?
#Test
public void subscriberTest() {
Mono<String> m = Mono.just("Hello!");
Subscriber<String> s = new Subscriber<String>() {
#Override
public void onSubscribe(Subscription s) {
System.out.println("Subscription "+s);
}
#Override
public void onNext(String t) {
System.out.println("onNext "+t);
}
#Override
public void onError(Throwable t) {
System.out.println("Throwable "+t);
}
#Override
public void onComplete() {
System.out.println("onComplete");
}
};
m.subscribe(s);
Mono<String> m1 = Mono.just("Bye!");
m1.subscribe(System.out::println);
}
Though the variable m1 subscription with method reference works fine. Here console output:
Subscription reactor.core.publisher.StrictSubscriber#4b168fa9
Bye!
Here I expect to see Hello! phrase too.
https://www.reactive-streams.org/reactive-streams-1.0.0-javadoc/org/reactivestreams/Subscriber.html#onSubscribe-org.reactivestreams.Subscription- Here it is stated that No data will start flowing until Subscription.request(long) is invoked.
Related
I am developing an application that returns Multi<String>, I would like to make some modifications to it, so I have added some methods, but for some reason it does not enter the next method at all.
My other methods are working absolutely fine. Because I am able to collect it and add it to a List, but I want to do some execution asynchronously, so using this approach.
private final ManagedExecutor managedExecutor;
public void writeTo(StreamingInfo streamingInfo) {
streamingInfo
.getEvents()
.runSubscriptionOn(managedExecutor)
.subscribe()
.withSubscriber(
new Subscriber < String > () {
#Override
public void onSubscribe(Subscription s) {
System.out.println("OnSubscription Method");
System.out.println("ON SUBS END");
}
#Override
public void onNext(String event) {
System.out.println("On Next Method");
}
#Override
public void onError(Throwable t) {
System.out.println("OnError Method");
}
#Override
public void onComplete() {
System.out.println("On Complete Method");
}
});
}
I get the following output:
OnSubscription Method
ON SUBS END
Which means that your subscription is not working for some reason. If I do not add subscription and directly collect to List then everything works as expected. Can anyone suggest what am I doing wrong here?
This is because the underlying Reactive Streams specification that SmallRye Mutiny implements has a built-in backpressure mechanism. The client (in your case your subscriber) needs to request the next item manually from the producer (events) otherwise, no item is sent down the reactive pipeline.
You need to save the Subscription object you receive in the onSubscribe method and call its request(long) method when you can process next item(s):
.withSubscriber(
new Subscriber<String>() {
private Subscription subscription;
#Override
public void onSubscribe(Subscription s) {
System.out.println("OnSubscription Method");
System.out.println("ON SUBS END");
subscription = s;
subscription.request(1);
}
#Override
public void onNext(String event) {
System.out.println("On Next Method");
subscription.request(1);
}
#Override
public void onError(Throwable t) {
System.out.println("OnError Method");
}
#Override
public void onComplete() {
System.out.println("On Complete Method");
}
});
In SmallRye there is also an easier way to do this:
events
.onSubscription()
.invoke(() -> {
System.out.println("OnSubscription Method");
System.out.println("ON SUBS END");
})
.onItem()
.invoke(event -> System.out.println("On Next Method"))
.onFailure()
.invoke(t -> System.out.println("OnError Method"))
.onCompletion()
.invoke(() -> System.out.println("On Complete Method"))
.subscribe()
.with(value -> {});
Once the new Object is been added in a pipeline i can see the value(obj.url) of the new Object being printed without getting executing the Flux part.
Note: If i restart my application, it able to read all the objects and executes completely.
Issue: Seems like flux.interval is not getting subscribed for the
new events.
The new real time value will be added into a Map<String, BaseSubscriber>
Once added it will invoke startPipeline() method where it get printed with a new value but it wont execute after that . So here the real time added objects wont be executed with getMessages() and doSomeTask() method.
class pipelineProcess extends BaseSubscriber<Data>{
private final XYZ obj;
#PostConstruct
public void startPipeline() {
log.info("New Objected read {} ",obj.getUrl())
Flux.interval(Duration.ofMillis(500))
.flatMapIterable(aLong -> getMessages())
.flatMap(message -> doSomeTask(message))
.subscribe(this);
}
}
#Override
protected void hookOnSubscribe(Subscription subscription) {
log.info("Hook on Subscribe called for {} : ",obj.getUrl());
request(1);
}
#Override
protected void hookOnNext(ScanRequestData next) {
log.info("Hook on Next called for {}: ", obj.getUrl());
request(1);
}
#Override
protected void hookOnError(Throwable throwable) {
log.error("hookOnError exception", throwable);
request(1);
}
#Override
protected void hookOnComplete() {
log.warn("The reactor pipeline of {} has been completed.", obj.getUrl());
}
#Override
protected void hookOnCancel() {
log.warn("The reactor pipeline of {} has been cancelled.", obj.getUrl());
}
}
Can anyone tell me how to overcome this issue
I am playing around with 'ListenableFutureCallback'. onSuccess() works fine, but onFailure is never called. Below is some example code.
#Service
public class AsyncClass {
#Async
public ListenableFuture<String> execute(Callable<String> callable) throws Exception {
String str = callable.call();
//To force an exception to occur
str.toString();
return new AsyncResult<>(str);
}
}
public void futureMethod(String str) throws Exception {
AsyncClass asyncClass = new AsyncClass();
ListenableFuture<String> future = asyncClass.execute(() -> {
return str;
});
future.addCallback(new ListenableFutureCallback<Object>() {
#Override
public void onFailure(Throwable ex) {
System.out.println("FAIL");
}
#Override
public void onSuccess(Object result) {
System.out.println("SUCCESS");
}
});
}
onSuccess works correct.
futureMethod("test value");
Console: SUCCESS
onFailure does however not work.
futureMethod(null);
Console: java.lang.NullPointerException: null
You aren't using listenable futures. You are executing code and putting the result in a listenable future.
In order for onFailure to trigger you need to run the failing code inside the future or complete the future with an exception.
For example
Futures.immediateFailedFuture(new RuntimeException("woops"));
Listenable futures are generally expected to be retrieved from ListeningExecutorServices. ListenableFuture, unlike CompletableFuture, isn't completable externally.
I have a DisposableSubscriber to a Flowable. The Flowable runs for some timeUntilTimeout, but in some situations I need to kill it earlier. Right now I call .dispose() on the DisposableSubscriber but the Flowable continues to emit events. Eventually the Flowable times out and .doOnCancel() is called.
I have the following code:
private Disposable mDisposableSubscription = null;
public void start() {
mDisposableSubscription = getFlowable()
.timeout(timeUntilTimeout, TimeUnit.MILLISECONDS)
.subscribeWith(new DisposableSubscriber<T>() {
#Override
public void onComplete() {
}
#Override
public void onError(Throwable throwable) {
dispose();
}
#Override
public void onNext(T t) {
// Do something
}
});
}
public void stop() {
// Log "dispose"
mDisposableSubscription.dispose();
}
private Flowable<T> getFlowable() {
return Flowable.create(new FlowableOnSubscribe<T>() {
public void subscribe(FlowableEmitter<T> emitter) {
startSomeAsyncProcess();
}
}).doOnCancel(new Action() {
public void run() {
// Log "do on cancel"
stopSomeAsyncProcess();
}
});
}
Calling stop() to dispose of the DisposableSubscriber before the Flowable times out means events emitted by the Flowable are no longer handled, but the Flowable continues emitting events and the async process continues running. I was under the impression that calling .dispose() downstream of the Flowable kills the Flowable by calling .doOnCancel(), but this does not appear to be the case. What am I missing?
The flowable is getting disposed, but you are not checking it on your Flowable.create function, so what happens is that the startSomeAsyncProcess() ignores it and keeps going.
To solve the issue, you should check the emitter.isDisposed() flag to know if you should stop emitting.
Example:
Flowable<T> getFlowable() {
return Flowable.create(new FlowableOnSubscribe<T>() {
public void subscribe(FlowableEmitter<T> emitter) {
while(!emitter.isDisposed()) {
emitter.onNext(...);
}
}
});
}
If that startSomeAsyncProcess() function doesn't allow you to check the flag, surely there is some way to cancel it. Then you can attach a cancellable:
Flowable<T> getFlowable() {
return Flowable.create(new FlowableOnSubscribe<T>() {
public void subscribe(FlowableEmitter<T> emitter) {
startSomeAsyncProcess();
emitter.setCancellable(() -> stopSomeAsyncProcess());
// I don't remember if it's setCancellable() or setDisposable()
}
});
}
Update: the methods setCancellable(...) and setDisposable(...) should behave equally, they just take different arguments.
In an Android project that uses RxJava 2, I create a Flowable like this in the onCreate of my initial activity:
Flowable.create(new MyFlowableOnSubscribe1(), BackpressureStrategy.BUFFER)
.doOnComplete(new MyAction())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new MySubscriber());
The implementation of the FlowableOnSubscribe is:
public class MyFlowableOnSubscribe1 implements FlowableOnSubscribe<String> {
public static final String TAG = "XX MyFlOnSub1";
#Override
public void subscribe(FlowableEmitter<String> emitter) {
Log.i(TAG, "subscribe");
emitter.onNext("hello");
emitter.onComplete();
}
}
This is the subscriber implementation:
public class MySubscriber implements Subscriber<String> {
public static final String TAG = "XX MySubscriber";
#Override
public void onSubscribe(Subscription s) {
Log.i(TAG, "onSubscribe");
}
#Override
public void onComplete() {
Log.i(TAG, "onComplete");
}
#Override
public void onError(Throwable e) {
Log.i(TAG, "onError");
}
#Override
public void onNext(String s) {
Log.i(TAG, "onNext: " + s);
}
}
And the action implementation is:
public class MyAction implements Action {
public static final String TAG = "XX MyAction";
#Override
public void run() {
Log.i(TAG, "run");
}
}
In my output, I'm expecting to a log statement from onNext, but I don't see one. Instead, this is my entire output:
02-23 17:56:31.334 24176-24176/com.ebelinski.rxjavaexperimentproject I/XX MySubscriber: onSubscribe
02-23 17:56:31.334 24176-24219/com.ebelinski.rxjavaexperimentproject I/XX MyFlOnSub1: subscribe
02-23 17:56:31.334 24176-24219/com.ebelinski.rxjavaexperimentproject I/XX MyAction: run
This indicates that onNext never runs, and onComplete doesn't even run either. But MyAction runs successfully.
Here's what happens when I comment out the call to onNext:
02-23 17:58:31.572 24176-24176/com.ebelinski.rxjavaexperimentproject I/XX MySubscriber: onSubscribe
02-23 17:58:31.572 24176-26715/com.ebelinski.rxjavaexperimentproject I/XX MyFlOnSub1: subscribe
02-23 17:58:31.572 24176-26715/com.ebelinski.rxjavaexperimentproject I/XX MyAction: run
02-23 17:58:31.652 24176-24176/com.ebelinski.rxjavaexperimentproject I/XX MySubscriber: onComplete
In this case onNext of course doesn't run, but at least onComplete runs.
I expected that I would see onComplete run in both cases, and onNext run when I call emitter.onNext. What am I doing wrong here?
You need to manually issue a request otherwise no data will be emitted when extending Subscriber directly:
#Override
public void onSubscribe(Subscription s) {
Log.i(TAG, "onSubscribe");
s.request(Long.MAX_VALUE);
}
Alternatively, you could extend DisposableSubscriber or ResourceSubscriber.
How are you testing it? Is it possible your main thread exits before the Observable has a chance to emit the result because you are using Schedulers.IO for the subscribe thread. Also, your observeOn will not do anything as it is only used for further downstream operators.