Custom timeout message in Retrofit - java

I need to set a custom message for the user when Retrofit reaches the timeout. I've searched through stackoverflow and couldn't find a solution for this. I've also searched through github and found this (probably the line that's responsible for the message I'm seeing at this point):
ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
This is in the class okhttp/src/main/java/okhttp3/internal/connection/RealConnection.java
So I went to that class, and since it belongs to OkHttp lib, it wouldn't let me edit this message.
Does anyone have any idea how I could use my own custom timeout handler?
Relevant build.gradle entries:
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
EDIT:
Both solutions given below would kind-of work probably, but it's really ugly and I'd have to actually change the onFailure method in about 70-80 places which is less than ideal.

On failure block of retrofit network call.
#Override
public void onFailure(Call<Void> call, Throwable t) {
if (t.toString().contains("SocketTimeoutException")) {
// set your custom message here
//view.showToast(R.string.poor_internet_connection);
} else {
.....
}
}

In your error callback method use below code
#Override
public void error(Throwable exception) {
String errorMsg
if (exception instanceof java.net.SocketException || exception instanceof java.net.SocketTimeoutException) {
errorMsg = "Your custom message here"
}
}

Related

Coroutines delegate exceptions

Currently, I have some scenario like this where I have java interface callback which looks something like this.
Java Callback
interface Callback<T> {
void onComplete(T result)
void onException(HttpResponse response, Exception ex)
}
Suspending function for the above look like this
suspend inline fun <T> awaitCallback(crossinline block: (Callback<T>) -> Unit) : T =
suspendCancellableCoroutine { cont ->
block(object : Callback<T> {
override fun onComplete(result: T) = cont.resume(result)
override fun onException(e: Exception?) {
e?.let { cont.resumeWithException(it) }
}
})
}
My calling function looks like this
fun getMovies(callback: Callback<Movie>) {
launch(UI) {
awaitCallback<Movie> {
// I want to delegate exceptions here.
fetchMovies(it)
}
}
What I'm currently doing to catch exception is this
fun getMovies(callback: CallbackWrapper<Movie>) {
launch(UI) {
try{
val data = awaitCallback<Movie> {
// I want to delegate exceptions here.
fetchMovies(it)
}
callback.onComplete(data)
}catch(ex: Exception) {
callback.onFailure(ex)
}
}
}
// I have to make a wrapper kotlin callback interface for achieving the above
interface CallbackWrapper<T> {
fun onComplete(result: T)
fun onFailure(ex: Exception)
}
Questions
The above works but is there any better way to do this?? One of the main thing is I'm currently migrating this code from callback so I have ~20 api calls and I don't want to add try/catch everywhere to delegate the result along with the exception.
Also, I'm only able to get exception from my suspending function is there any way to get both HttpResponse as well as the exception. Or is it possible to use existing JAVA interface.
Is there any better way to delegate the result from getMovies without using callback??
Is there any better way to delegate the result from getMovies without using callback?
Let me start with some assumptions:
you're using some async HTTP client library. It has some methods to send requests, for example httpGet and httpPost. They take callbacks.
you have ~20 methods like fetchMovies that send HTTP requests.
I propose to create an extension suspend fun for each HTTP client method that sends a request. For example, this turns an async client.httpGet() into a suspending client.awaitGet():
suspend fun <T> HttpClient.awaitGet(url: String) =
suspendCancellableCoroutine<T> { cont ->
httpGet(url, object : HttpCallback<T> {
override fun onComplete(result: T) = cont.resume(result)
override fun onException(response: HttpResponse?, e: Exception?) {
e?.also {
cont.resumeWithException(it)
} ?: run {
cont.resumeWithException(HttpException(
"${response!!.statusCode()}: ${response.message()}"
))
}
}
})
}
Based on this you can write suspend fun fetchMovies() or any other:
suspend fun fetchMovies(): List<Movie> =
client.awaitGet("http://example.org/movies")
My reduced example is missing the parsing logic that turns the HTTP response into Movie objects, but I don't think this affects the approach.
I'm currently migrating this code from callback so I have ~20 api calls and I don't want to add try/catch everywhere to delegate the result along with the exception.
You don't need a try-catch around each individual call. Organize your code so you just let the exception propagate upwards to the caller and have a central place where you handle exceptions. If you can't do that, it means you've got a specific way to handle each exception; then the try-catch is the best and idiomatic option. It's what you would write if you had a plain blocking API. Especially note how trivial it is to wrap many HTTP calls in a single try-catch, something you can't replicate with callbacks.
I'm only able to get exception from my suspending function is there any way to get both HttpResponse as well as the exception.
This is probably not what you need. What exactly do you plan to do with the response, knowing that it's an error response? In the example above I wrote some standard logic that creates an exception from the response. If you have to, you can catch that exception and provide custom logic at the call site.
I am not so sure whether you really need that awaitCallback or not.
If you really have lots of Callback already in place and that's why you used it then your functions will probably already have everything in place that works correctly with the Callback, e.g. I expect some methods as follows:
fun fetchMovies(callback : Callback<List<Movie>>) {
try {
// get some values from db or from a service...
callback.onComplete(listOf(Movie(1), Movie(2)))
} catch (e : Exception) {
callback.onFailure(e)
}
}
If you do not have something like this in place, you may not even need awaitCallback at all. So if your fetchMovies function rather has a signature as follows:
fun fetchMovies() : List<Movie>
and in getMovies you pass your Callback, then all you need is probably a simple async, e.g.:
fun getMovies(callback: Callback<List<Movie>>) {
GlobalScope.launch { // NOTE: this is now a suspend-block, check the parameters for launch
val job = async { fetchMovies() }
try {
callback.onComplete(job.await())
} catch (e: Exception) {
callback.onException(e)
}
}
}
This sample can of course be changed to many similar variants, e.g. the following will also work:
fun getMovies(callback: Callback<List<Movie>>) {
GlobalScope.launch { // NOTE: this is now a suspend-block, check the parameters for launch
val job = async { fetchMovies() } // you could now also cancel/await, or whatever the job
job.join() // we just join now as a sample
job.getCompletionExceptionOrNull()?.also(callback::onFailure)
?: job.getCompleted().also(callback::onComplete)
}
}
You could also add something like job.invokeOnCompletion. If you just wanted to pass any exception to your callback in your current code, you could just have used callback.onException(RuntimeException()) at the place where you put your comment I want to delegate exceptions here..
(note that I am using Kotlin 1.3 which is a RC now...)

Java Functions, Returns, and Optionals

I am trying to create a client library that reads JSON from an external file online. I already know about the function interfaces and optionals, but I was wondering if there is a way to allow users to supply callback functions such that the parent function exits completely. For JavaScript, such a function is as follows:
file.read('hello', function(err, data) {
// something here
});
Basically, I wish to do the same in Java. How can I do this such that the error callback supersedes the read function? What I mean is that in the event that the error callback is called, then read should not return a value at all. If the callback is not called then the read should return the value.
You could have the user pass in a function and then just not do anything with it if there is no error.
This example assumes that you have a custom class called Error that the caller is aware of and would like to interact with in case of an error.
public void read (String str, Function<Error,Void> errorFunc)
{
//interact w/ libraries, boolean error = true or false
//if there is an error, variable err of type Error contains information
if (error)
{
errorFunc.apply(err);
}
}
In Java upto 1.7 the only way to achieve javascript like callbacks is thru interface. The api user who calls your method read has the liberty of implementing what he feels needs to be done to handle the error by writing an implementation class for the interface at the invocation point.
public String read(String options,IErrorCallBack errorHandler) throws Exception {
try {
// When everything works fine return what you think should be returned.
return "Success";
}
catch(Exception e) {
// On Error call the function on the error handler.
errorHandler.doSomething();
throw e;
}
}
public interface IErrorCallBack {
public void doSomething();
}
// The invocation point.
read("myString", new IErrorCallBack() {
public void doSomething() {
// Your implementation.
}
});

javax.net.ssl.SSLHandshakeException: Connection reset by peer

I use following code to call an Azure mobile backend API in my Android app,
try {
mobileClient.invokeApi("CustomTransaction", senderToCheck,
Boolean.class, new ApiOperationCallback<Boolean>() {
#Override
public void onCompleted(Boolean result,
Exception error, ServiceFilterResponse response) {
if (error == null) {
CheckSender(result);
} else {
dial.dismiss();
Crouton.makeText(MyActivity.this,
"Eror Occured with service",
Style.ALERT).show();
}
}
});
} catch (SecurityException e) {
Log.d(TAG, "CouldNotConnectToSocket", e);
e.printStackTrace();
} catch (IllegalArgumentException e) {
Log.d(TAG, "CouldNotConnectToSocket", e);
e.printStackTrace();
}
Other information:
CustomTransaction - API Controller name;
senderToCheck - JSON parsable data transfer object;
Boolean.class - return type; and 4th parameter is the callback method
All objects are JSON parsable and this worked like several days ago.
So this API call/Azure call always times out giving a What does "connection reset by peer" mean? ,SSLHandShakeExceptionand and most of the time Connect gets Timed out.
Main cause for the problem is com.microsoft.windowsazure.mobileservices.MobileServiceException: Error while processing request.
I tried re-publishing my asp.net web app several times but it never hits controller action where my debugger point is placed when debugging the service call remotely.
I checked if my service is down, found it is up & running then checked Azure management portal logs, found out traceApi messages of some controller action methods. and of SQL Cpu usages and Data out packet sizes., but I never gets a proper reply from anywhere to solve this problem for two weeks now.
In case,if I am correct, think the solution for this problem lies in http://www.webapper.com/blog/index.php/2007/02/09/troubleshooting-javaxnetsslsslhandshakeexception/ but Im not pretty sure on doing it.
Please advise me on getting this fixed

The Best Way to Debug Retrofit Error Messages

I'm new to Retrofit. I have used Volley, and I kind of like Retrofit. I was just about to select Retrofit when I ran into this very non-descriptive error message when trying to do a POST.
Exception in thread "main" retrofit.RetrofitError
at retrofit.RetrofitError.httpError(RetrofitError.java:37)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:413)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:282)
at myapi.api.$Proxy7.logon(Unknown Source)
at myapi.api.TestDriver.main(TestDriver.java:94)
Well, I must say that this type of error message is about as useful as a warm jacket in the Sahara.
Does anyone even know where to begin with debugging this type of message? I really am not about to delegate to a REST api that does not provide useful error messages.
You probably want to add a catch clause to TestDriver.main:
try {
service.logon();
} catch (RetrofitError e) {
System.out.println(e.getResponse().getStatus());
}
Create a custom ErrorHandler for Retrofit.
I found that catching the error didn't provide a whole lot of extra information but creating a custom ErrorHandler for Retrofit allowed me to dig deeper into the actual error, like so:
class MyErrorHandler implements ErrorHandler {
#Override public Throwable handleError(RetrofitError cause) {
Response r = cause.getResponse();
if (r != null && r.getStatus() == 401) {
return new UnauthorizedException(cause);
}
return cause;
}
}
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.github.com")
.setErrorHandler(new MyErrorHandler())
.setLogLevel(RestAdapter.LogLevel.FULL) // Do this for development too.
.build();
From the Custom Synchronous Error Handling section on Retrofit Page.
Set the Log Level to FULL as well, as shown in above config code.
Unfortunately RetrofitError (1.6.1) was tricky. 401's made getResponse() always return null which made it hard to tell if its a connection issue or authentication issue. At least for me, I had to look at the message to get the 401 error. Hopefully this helps someone else trying to do something similar.
public class RetrofitErrorHandler implements ErrorHandler {
#Override
public Throwable handleError(RetrofitError cause) {
if (cause.isNetworkError()) {
if(cause.getMessage().contains("authentication")){
//401 errors
return new Exception("Invalid credentials. Please verify login info.");
}else if (cause.getCause() instanceof SocketTimeoutException) {
//Socket Timeout
return new SocketTimeoutException("Connection Timeout. " +
"Please verify your internet connection.");
} else {
//No Connection
return new ConnectException("No Connection. " +
"Please verify your internet connection.");
}
} else {
return cause;
}
}
}

quickfix event after sending message

I am doing something with fix protocol using quickfix library.
I wrote class like this:
public class ApplicationImpl implements Application {
...
#Override
public void toApp(Message arg0, SessionID arg1) throws DoNotSend {
//this is invoked before sending message
}
...
}
I wonder how to invoke some method after sending message?
QuickFIX does not offer a after-message-send callback.
You need to have this somewhere in your code to send a message (not in the overriden methods):
Session.sendToTarget(outgoingMessage, orderSession);
That will execute some internal quickfixJ code and then call toApp(). The toApp() method allows you do modify the message before it is sent to the broker. But ideally in order to do something after you send you just need to add code after the call to Session.sendToTarget().
If you are adventurous, you can modify QuickFIX/J to do it. The MINA network layer does provide a messageSent callback. If you override that method in QFJ's InitiatorIoHandler (or AcceptorIoHandler) you could either directly process the messageSent event or propagate it to a modified Application interface.
If I undertand correctly. You need to do some action after you send a message. If it is correct I have the following example:
public static void send(Message message) {
boolean sent = Session.sendToTarget(message, mySessionId);
if (sent){
//do something
}else {
//something else
}
System.out.println("El mensaje se mandó: " + sent);
} catch (SessionNotFound e) {
System.out.println(e);
}
}

Categories

Resources