I'm stuck to understand why RxJava does not capture my exception and it just crashes. The exception occurs while getting the object to emit.
Here is how my code looks like:
TestService testService = new TestService();
Single.just(testService.testGetUser())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<User>() {
#Override
public void onSuccess(#NonNull User user) {
Log.d(TAG, "onSuccess(): Launching Home Activity");
Intent intent = new Intent(LoginActivity.this, HomeActivity.class);
startActivity(intent);
}
#Override
public void onError(#NonNull Throwable e) {
Log.d(TAG, "onError()");
}
});
My TestService class looks like:
public class TestService {
public User logInUser(LogInData logInData) throws RuntimeException {
Log.d(TAG, "logInUser(): Logging user in");
User user = this.guideMeAroundData.logInUser(logInData);
return user;
}
}
In my logInUser() method I'm throwing an exception if the user is not valid. This causes my whole Android app to crash.
It looks like this is not the correct way to do it. Could anybody please tell how should I handle an scenario where an exception is thrown while getting the object(s) to be emitted?
This is a very common (and baffling) misunderstanding of Java syntax and operations. The code
Single.just(testService.testGetUser())
is equivalent to
User tmp1 = testService.testGetUser();
Single.just(tmp1)
where testGetUser executes before RxJava even gets involved thus any exception it throws happens before that.
You are probably looking for the
Single.fromCallable(() -> testService.testGetUser())
or simply define the Retrofit API as Single<User> logInUser(LogInData data).
Unchecked exceptions are catched by the RxJava for the most part.
But for checked exceptions you need to provide your own try{}catch(){}
block.
One example would be -
String transform(String input) throws IOException;
Observable.just("Hello!")
.map(input -> {
try {
return transform(input);
} catch (Throwable t) {
throw Exceptions.propagate(t);
}
})
Check out this post for info, it explained very well.
http://blog.danlew.net/2015/12/08/error-handling-in-rxjava/
Related
I've written the following code in my MainActivity of an Android app. When I run the following code, it doesn't throw any exception and onError() also doesn't get called. However I see onSuccess: testing starts twice but I don't see onSuccess: testing ends. Why isn't onError() being called and/or why isn't the app crashing?
Single.timer(1000, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.computation())
.subscribeWith(new DisposableSingleObserver<Long>() {
#Override
public void onSuccess(Long initiationTimeStamp) {
String s = null;
Log.d(TAG, "onSuccess: testing starts");
Log.d(TAG, "onSuccess:test "+ s.isEmpty());
Log.d(TAG, "onSuccess: testing ends");
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
});
You are calling s.isEmpty() on a NULL String, thats why it ends at first print. That said onSuccess() does not throw anything, so it just stops execution when NullPointerException is thrown (it is silently handled inside RxJava for you). As soon as you subscribe to observable, you get initial value in onSuccess(), then if it changes or you resubscribe you get another value in onSuccess(), thats why it gets called twice. And because onError() is for errors occurring along the operation chain, you do not get an error in onSuccess() when exception is thrown.
This behaviour is intentional. According to Rx Contract, an observer should not receive both onSuccess() and onError(). You need to handle the exception in onSuccess() by yourself.
For example:
Single.timer(1000, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.computation())
.subscribeWith(new DisposableSingleObserver<Long>() {
#Override
public void onSuccess(Long initiationTimeStamp) {
try {
String s = null;
Log.d(TAG, "onSuccess: testing starts");
Log.d(TAG, "onSuccess:test "+ s.isEmpty());
Log.d(TAG, "onSuccess: testing ends");
}
catch (Throwable ex) {
// tell the upstream we can't accept any more data (OPTIONAL)
dispose();
// pass error to error handler
onError(ex);
}
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
});
Good luck :)
onError is for errors that happen along the operator chain. What you do in onSuccess is already at the end of it, and will not be reported in onError.
When Tomcat session times out, I want to redirect my user to the homepage of my GWT app, so that they can login again. To force this, I'm trying to use the StatusCodeException thrown by GWT when the user tries to perform any operation after their session times out -
SEVERE: com.google.gwt.user.client.rpc.StatusCodeException: 0
To achieve this, I'm using the following code -
public void onModuleLoad() {
GWT.UncaughtExceptionHandler uncaughtExceptionHandler = new GWT.UncaughtExceptionHandler() {
public void onUncaughtException(Throwable e) {
if (e instanceof StatusCodeException) {
logger.log(Level.ERROR, "Exception caught!");
logger.log(Level.ERROR, ((StatusCodeException) e).getStatusCode());
}
}
};
GWT.setUncaughtExceptionHandler(uncaughtExceptionHandler);
try {
// rest of the code in onModule() - I'm expecting any operation to throw StatusCodeException when session times out.
} catch (RuntimeException ex) {
uncaughtExceptionHandler.onUncaughtException(ex);
}
}
This is not working. Instead of getting caught by the code, the StatusCodeException is being displayed on the console. What am I doing wrong here?
The idea is to catch StatusCodeException and use its getStatusCode() method to find out if the HTTP error code is 403. If it is, I want to use Window.Location.assign("https://example.com/redirect"); to redirect them to a login page.
onFailure(Throwable caught) {
logger.error(caught);
}
Your AsyncCallback.onFailure is doing exactly what you asked it to do - it is logging the error, but not throwing it. Since it wasn't thrown, the uncaught exception handler doesn't handle it (it can't be not-caught, if it wasn't thrown... if that makes sense).
One option could be that you could populate the method with throw caught, but java won't like this. Instead, the easiest answer to your specific on is simply to pass it to the handler:
onFailure(Throwable caught) {
GWT.getUncaughtExceptionHandler().onUncaughtException(ex);
}
One other option you have: since no AsyncCallback will ever throw this, putting the StatusCodeException in the UncaughtExceptionHandler seems a bit odd. Instead, consider making your own AsyncCallback base class, something like this:
public abstract class NetworkAsyncCallback<T> implements AsyncCallback<T> {
#Override
public void onFailure(Throwable t) {
if (e instanceof StatusCodeException) {
logger.log(Level.ERROR, "Exception caught!");
logger.log(Level.ERROR, ((StatusCodeException) e).getStatusCode());
}
}
}
Now, when you make a call, you just have to pass in a new NetworkAsyncCallback<T> and only implement onSuccess. You can skip onFailure if all it was going to do was pass the exceptions to the uncaught handler. Or, if you have some other logic, you can override onFailure, handle the appropriate exceptions, and call super.onFailure(caught) with any other errors so that the superclass handles it.
myServer.getSomeData(param, new NetworkAsyncCallback<Result>() {
#Override
public void onSuccess(Result result) {
//...
}
// Skip onFailure, or if you need custom logic, implement it,
// and call super only if the exception isn't part of that logic
});
I'm a bit new to Android, Java, and Azure, and I'm using the Azure MobileServiceClient class to try to call various APIs in my backend. The problem I'm having is that MobileServiceClient's methods all seem to be async, using ListenableFutures.
This would be just fine, except that I want to use these methods in a helper class that also does other stuff that ought to be async (so it extends AsyncTask). However, since the MobileServiceClient call is async, it causes the AsyncTask to return prematurely. I want the AsyncTask not to call its onPostExecute method until the MobileServiceClient method has returned.
How do I avoid this issue? Do I need to change my architecture? Is it possible to put a MobileServiceClient call inside another async task and have it block it?
#Override
protected Boolean doInBackground(Void... params) {
Log.i(TAG, "Doing background task");
if(mTaskType==tTaskType.LOGIN_TASK){
login();
//do other stuff here that should be async
}
return true;
}
private void login(){
Log.i(TAG, "Doing login task...");
ListenableFuture<JsonElement> result = mClient.invokeApi("login", mJSONHelper.makeLoginObject(thisUser));
Futures.addCallback(result, new FutureCallback<JsonElement>() {
#Override
public void onFailure(Throwable exc) {
error.setError(ErrorHelper.Error.NETWORK_ERROR);
}
#Override
public void onSuccess(JsonElement result) {
}
});
}
I'm going to preface this with the caveat that I'm not very familiar with Android either. However from my experience on other platforms and a quick search of the APIs this is the approach I think you should take. I'm also not promising that code code snippets will compile as I haven't checked that, but they should be close to doing so.
Your login method should return a ListenableFuture<T> and then the doInBackground method can then add it's own callback which is executed when the login completes.
If you want something else to be able to wait for the doInBackground task to complete then that should also return a ListenableFuture<T> which can be done by using the Futures.transform method to chain together a series of async calls.
Here is what I think it should look like:
protected void doInBackground(Void... params) {
Log.i(TAG, "Doing background task");
if(mTaskType==tTaskType.LOGIN_TASK){
var loginFuture = ListenableFuture<UserDetail> login();
Futures.addCallback(loginFuture, new FutureCallback<UserDetail>() {
#Override
public void onSuccess(UserDetail userDetail)
{
// do other stuff here that should be async
// also optionally you could implement this as a transform
// style thing to and return another future from this `doInBackground`
// method so other parts of your code could know when it is completed.
}
#Override
public void onFailure(Throwable exc) {
// I'd quite likely move the error handling from the login method here
// as that way it can also handle any exceptions caused by the transform
// from json to user detail as well.
}
})
}
}
private ListenableFuture<UserDetail> login(){
Log.i(TAG, "Doing login task...");
ListenableFuture<JsonElement> loginFutureResult = mClient.invokeApi("login", mJSONHelper.makeLoginObject(thisUser));
Futures.addCallback(loginFutureResult, new FutureCallback<JsonElement>() {
#Override
public void onFailure(Throwable exc) {
// This is just to keep with what your style is, for recording the error
// I think you might be better off handling it at a higher level and
// also you might want to check `exc` to see if it was an actual network
// error and not for example just failed credentials or something.
error.setError(ErrorHelper.Error.NETWORK_ERROR);
}
#Override
public void onSuccess(JsonElement result) {
Log.i(TAG, "The login was successful");
}
});
// lets pretend that instead of returning the JSON response
// you wanted to map it to a user detail before returning, just to show how to do that.
AsyncFunction<JsonElement, UserDetail> transformUserJsonFunction =
new AsyncFunction<JsonElement, UserDetail>() {
public ListenableFuture<UserDetail> apply(JsonElement userJson) {
// some code to map the json element to user detail
UserDetail userDetail = new UserDetail(userJson);
return Futures.immediateFuture(userDetail);
}
};
return Futures.transform(loginFutureResult, transformUserJsonFunction);
}
I hope that points you in the right direction.
Well, I did it with a flag - it's not pretty though. I'm still interested in any ways to do this that are more elegant or correct. Or is this actually the right way to do it?
private void login(){
Log.i(TAG, "Doing login task...");
isLoginFinished= false;
ListenableFuture<JsonElement> result = mClient.invokeApi("login", mJSONHelper.makeLoginObject(thisUser));
Futures.addCallback(result, new FutureCallback<JsonElement>() {
#Override
public void onFailure(Throwable exc) {
error.setError(ErrorHelper.Error.NETWORK_ERROR);
isLoginFinished= true;
}
#Override
public void onSuccess(JsonElement result) {
Log.i(TAG, "Login call was successful, parsing result:" + result.toString());
isLoginFinished= true;
}
});
while(!isLoginFinished);
}
I'm getting to grips with the Spark Framework and I'm trying to understand the best way of handling exceptions in a uniform way for multiple Routes.
At the moment I have a number of Routes which all handle exceptions along the lines of:
...
catch (final Exception e) {
...
response.status(418);
return e.getMessage();
}
...
This leaves a lot to be desired, mainly the exception logic is duplicated between them. I know that it can be improved by refactoring but I was wondering if there's something similar to the ExceptionHandler mechanism in Spring where you can perform an action when a particular exception is thrown, e.g.:
#ExceptionHandler(Exception.class)
public void handleException(final Exception e, final HttpServletRequest request) {
...executed for the matching exception...
}
So, is there a Spark-esque mechanism for exception handling? I've checked the documentation and come up short. If there isn't, I'll go ahead with my refactoring plans. Thanks.
You can handle exceptions like so:
get("/throwexception", (request, response) -> {
throw new NotFoundException();
});
exception(NotFoundException.class, (e, request, response) -> {
response.status(404);
response.body("Resource not found");
});
Example taken from the Spark docs.
I've been dealing with this very issue. This is what I came up with. It will need tweaking to your environment.
public class ExceptionHandler extends MustacheTemplateHandler
{
private final WrappedHandler inter;
public abstract static class WrappedHandler
{
public abstract Object handle(Request req, Response res);
}
public static ExceptionHandler wrap(String path, WrappedHandler internal)
{
return new ExceptionHandler(path, internal);
}
private ExceptionHandler(String path, WrappedHandler internal)
{
super(path);
inter = internal;
}
#Override
public Object handle(Request req, Response res)
{
try
{
return inter.handle(req, res);
}
catch (Exception e)
{
e.printStackTrace();
return new ModelAndView(e, "errors");
}
}
}
and then (using import static):
get(wrap("/download", new DownloadHandler()));
post(wrap("/upload", new UploadHandler()));
I want to override the global Exception Handling in my RCP app. Whenever an uncaught Exception happens I want to log it (using java logging) and then exit the app. I have already overwritten the eventLoopException(Throwable exception) method in the ApplicationWorkbenchAdvisor class. But this catches only the event loop exceptions. As of now I have also overwritten the postStartup() method like this:
public void postStartup()
{
Policy.setStatusHandler(new StatusHandler()
{
#Override
public void show(IStatus status, String title)
{
LOGGER.log(Level.SEVERE, "Uncaught Exception", status.getException());
UnexpectedErrorDialog();
PlatformUI.getWorkbench().close();
}
});
}
It logs the exception in my log file and exits the app. But it's obviously not right and the exception is shown twice in the console, cause all I do is intercepting the showing of the exception in a gui dialog to the user. So how can I properly overwrite/change the global exception handling, so that my code (log) is used instead of the default one?
I would suggest you to use org.eclipse.ui.statusHandlers extension point
Thanks to sambi reddy's tip i have now overwritten AbstractStatusHandler in the ApplicationWorkbenchAdvisor class
#Override
public synchronized AbstractStatusHandler getWorkbenchErrorHandler() {
if (myStatusHandler == null) {
myStatusHandler = new MyStatusHandler();
}
return myStatusHandler;
}
MyStatusHandler extends AbstractStatusHandler and i have overwritten the handle method like this:
#Override
public void handle(StatusAdapter statusAdapter, int style)
{
if(statusAdapter.getStatus().matches(IStatus.ERROR) && ((style != StatusManager.NONE)))
{
LOGGER.log(Level.SEVERE, "Uncaught Exception", statusAdapter.getStatus().getException());
UnexpectedErrorDialog();
PlatformUI.getWorkbench().close();
}
}
seems to work right, only downside is that i still get 2 console outputs.