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.
Related
I've been given an interface which is like this:
MyInterface {
List<FirebaseVisionFace> getFaceList();
}
I have to implement it in a class (let's call it MyFirebaseFaceClass) in such a way that we can then do
List<FirebaseVisionFace> faceList = myFirebaseFaceClass.getFaceList()
The problem is that to get this list of faces, the following is required:
Task<List<FirebaseVisionFace>> result =
detector.detectInImage(image)
.addOnSuccessListener(
new OnSuccessListener<List<FirebaseVisionFace>>() {
#Override
public void onSuccess(List<FirebaseVisionFace> faces) {
// Task completed successfully
// ...
}
})
.addOnFailureListener(
new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// Task failed with an exception
// ...
}
});
Is it possible to implement this getFaceList() method without passing in a callback as a parameter and using the callback to get the list?
You might try using a CompletableFuture. In an implementation of your interface:
class MyImplementation implements MyInterface {
#Override
List<FirebaseVisionFace> getFaceList() {
final CompletableFuture<List<FirebaseVisionFace>> future = new CompletableFuture<>();
final DetectorOfSomeKindYouDidNotSpecifyAbove detector = // ... get detector
detector.detectInImage(image)
.addOnSuccessListener(
new OnSuccessListener<List<FirebaseVisionFace>>() {
#Override
public void onSuccess(List<FirebaseVisionFace> faces) {
// Task completed successfully
future.complete(faces);
}
})
.addOnFailureListener(
new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// Task failed with an exception
future.completeExceptionally(e);
}
});
try {
return future.get(); // blocks until completion by one of the listeners above
} catch (final RuntimeException throwMe) {
throw throwMe;
} catch (final InterruptedException interruptedException) {
Thread.currentThread().interrupt();
throw new IllegalStateException(interruptedException.getMessage(), interruptedException);
} catch (final Exception everythingElse) {
throw new IllegalStateException(everythingElse.getMessage(), everythingElse);
}
}
}
My error handling code above is stupid for brevity. All code above is untested and written off the cuff. I am assuming that the listeners are called when all discovery is done, and are called once. I'm also assuming that only one of the listeners is called, not both of them. I'm also assuming that they're called by another thread.
Lastly, do note that (as you seem to be aware) an interface like the kind you're trying to implement will inherently remove all benefits of the asynchronicity taking place in its underlying implementation.
Hopefully this will at least point you in the right direction!
If you work with asynchronous tasks in Java, you can't avoid callbacks. You use Task class that uses callbacks a lot. If you don't like this syntax, you can use lambdas:
Task<List<FirebaseVisionFace>> result =
detector.detectInImage(image)
.addOnSuccessListener((List<FirebaseVisionFace> faces) -> {
// Task completed successfully
})
.addOnFailureListener((#NotNull Exception e) -> {
// Task failed with an exception
})
You cannot avoid callbacks, but you can use method references as callbacks, so the whole code looks cleaner:
class MyFirebaseFaceClass implements MyInterface {
List<FirebaseVisionFace> faces;
Exception exception;
boolean done = false;
// Task completed successfully
private synchronized void onSuccess(List<FirebaseVisionFace> faces) {
this.faces = faces;
done = true;
notifyAll();
}
// Task failed with an exception
private synchronized void onError(Exception exception) {
this.exception = exception;
done = true;
notifyAll();
}
List<FirebaseVisionFace> getFaceList() {
detector.detectInImage(image)
.addOnSuccessListener(this::onSuccess)
.addOnFailureListener(this::onError);
synchronized (this) {
while (!done) {
wait();
}
if (exception == null) {
return faces;
} else {
throw new ExcecutionException(exception);
}
}
}
}
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.
I have method save. It saves entity using service which returns me com.google.common.util.concurrent.ListenableFuture<Void>. In case of fail I want to repeat save. I have next code for it:
public void save(Entity entity) {
ListenableFuture<Void> result = service.save(entity);
Futures.addCallback(result, new FutureCallback<Result>() {
#Override
public void onSuccess(Result callbackResult)
{
//do nothing
}
#Override
public void onFailure(Throwable throwable)
{
//some actions
save(entity);
}
});
}
But code above can be cause of StackOverflowException. If future will be done before addCallback then it will be recursive call. How can I optimize this code to remove recursion from it?
I use Retrofit with RxJava Observables and lambda expressions. I'm new to RxJava and cannot find out how to do the following:
Observable<ResponseBody> res = api.getXyz();
res.subscribe(response -> {
// I don't need the response here
}, error -> {
// I might be able to handle an error here. If so, it shall not go to the second error handler.
});
res.subscribe(response -> {
// This is where I want to process the response
}, error -> {
// This error handler shall only be invoked if the first error handler was not able to handle the error.
});
I looked at the error handling operators, but I don't understand how they can help me with my usecase.
Method 1: Keep the two Subscribers but cache the Observable.
Just keep everything as it is now, but change the first line to:
Observable<ResponseBody> res = api.getXyz().cache();
The cache will make sure that the request is only sent once but that sill both Subscribers get all the same events.
This way whether and how you handle the error in the first Subscriber does not affect what the second Subscriber sees.
Method 2: Catch some errors with onErrorResumeNext but forward all others.
Add onErrorResumeNext to your Observable to produce something like this (in the "inner" object):
Observable observable = Observable.error(new IllegalStateException())
.onErrorResumeNext(new Func1<Throwable, Observable<?>>() {
#Override
public Observable<?> call(Throwable throwable) {
if (throwable instanceof NumberFormatException) {
System.out.println("NFE - handled");
return Observable.empty();
} else {
System.out.println("Some other exception - panic!");
return Observable.error(throwable);
}
}
});
And only subscribe once (in the "outer" object):
observable.subscribe(new Subscriber() {
#Override
public void onCompleted() {
System.out.println("onCompleted");
}
#Override
public void onError(Throwable e) {
System.out.println("onError");
e.printStackTrace();
}
#Override
public void onNext(Object o) {
System.out.println(String.format("onNext: %s", String.valueOf(o)));
}
});
This way, the error is only forwarded if it cannot be handled in the onErrorResumeNext - if it can, the Subscriber will only get a call to onCompleted and nothing else.
Having side effects in onErrorResumeNext makes me a bit uncomfortable, though. :-)
EDIT: Oh, and if you want to be extra strict, you could use Method 3: Wrap every case in a new object.
public abstract class ResultOrError<T> {
}
public final class Result<T> extends ResultOrError<T> {
public final T result;
public Result(T result) {
this.result = result;
}
}
public final class HandledError<T> extends ResultOrError<T> {
public final Throwable throwable;
public Result(Throwable throwable) {
this.throwable = throwable;
}
}
public final class UnhandledError<T> extends ResultOrError<T> {
public final Throwable throwable;
public Result(Throwable throwable) {
this.throwable = throwable;
}
}
And then:
Wrap proper results in Result (using map)
Wrap handle-able errors in HandledError and
un-handle-able errors in UnhandledError (using onErrorResumeNext with an if clause)
handle the HandledErrors (using doOnError)
have a Subscriber<ResultOrError<ResponseBody>> - it will get notifications (onNext) for all three types but will just ignore the HandledErrors and handle the other two types.
I am using PublishSubject in the class that is responsible for synchronization. When the synchronization is done all the subscribers will be notified. The same happens in case of an error.
I've noticed that the next time I subscribe after an error has occured, it is immediately return to the subscriber.
So the class may look like this:
public class Synchronizer {
private final PublishSubject<Result> mSyncHeadObservable = PublishSubject.create();
private final ThreadPoolExecutor mExecutor = new ThreadPoolExecutor(1, 1,
10, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(true),
new ThreadPoolExecutor.DiscardPolicy());
public Observable<Result> syncHead(final int chunkSize) {
mExecutor.execute(new Runnable() {
#Override
public void run() {
try {
//Do some work which either returns a result or throws an error
//...
mSyncHeadObservable.onNext(Notification.createOnNext(/*some result*/));
} catch (Throwable error) {
mSyncHeadObservable.onError(Notification.<Result>createOnError(error));
}
}
});
Is there an observable which can just serve as an proxy? May be some other Rx approach?
UPDATE:
I've followed #akarnokd approach and emit the events wrapped into the RxJava Notification. Then unwrap them via flatMap(). So the clients of Synchronizer class won't need to do it.
//...
private PublishSubject<Notification<Result>> mSyncHeadObservable = PublishSubject.create();
public Observable<Result> syncHead(final int chunkSize) {
return mSyncHeadObservable.flatMap(new Func1<Notification<Result>, Observable<Result>>() {
#Override
public Observable<Result> call(Notification<Result> result) {
if (result.isOnError()) {
return Observable.error(result.getThrowable());
}
return Observable.just(result.getValue());
}
}).doOnSubscribe(
new Action0() {
#Override
public void call() {
startHeadSync(chunkSize);
}
});
}
private void startHeadSync(final int chunkSize) {
mExecutor.execute(new Runnable() {
#Override
public void run() {
try {
//Do some work which either returns a result or throws an error
//...
mSyncHeadObservable.onNext(Notification.createOnNext(/*some result*/));
} catch (Throwable error) {
mSyncHeadObservable.onError(Notification.<Result>createOnError(error));
}
}
});
}
//...
I'm not sure what your want to achieve with this setup, but generally, in order to avoid a terminal condition with PublishSubject, you should wrap your value and error into a common structure and always emit those, never any onError and onCompleted. One option is to use RxJava's own event wrapper, Notification, and your Subscribers should unwrap the value.
When a error occurred, the observable reached an terminal state.
If you want to continue to observe it, you should resubscribe to you observable with retry operator or use another error handling operators