The Best Way to Debug Retrofit Error Messages - java

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;
}
}
}

Related

How to handle exceptions in SpringBoot ListenableFuture

so I have a SpringBoot end point controller that starts like this:
#RequestMapping(value = "/post", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public Response post(#Valid #RequestBody Message message) throws FailedToPostException {
message.setRecieveTime(System.currentTimeMillis());
return this.service.post(message);
}
And the post function:
public Response post(Message message) throws FailedToPostException{
ListenableFuture<SendResult<String, Message>> future = kafkaTemplate.send("topicName", message);
future.addCallback(new ListenableFutureCallback<SendResult<String, Message>>() {
#Override
public void onSuccess(SendResult<String, Message> result) {
LOGGER.info("Post Finished. '{}' with offset: {}", message,
result.getRecordMetadata().offset());
}
#Override
public void onFailure(Throwable ex) {
LOGGER.error("Message Post Failed. '{}'", message, ex);
long nowMillis = System.currentTimeMillis();
int diffSeconds = (int) ((nowMillis - message.getRecieveTime()) / 1000);
if (diffSeconds >= 10) {
LOGGER.debug("timeout sending message to Kafka, aborting.");
return;
}
else {
post(message);
}
}
});
LOGGER.debug("D: " + Utils.getMetricValue("buffer-available-bytes", kafkaTemplate));
return new Response("Message Posted");
}
Now you can see, that we are trying to make sure, if a kafkaTemplate.send failed, we are going to recursively invoke post(message) again for up to 10 seconds, until the producer memory buffer clears and the message gets through.
The problems are:
We want to be able to return failure response to the endpoint's client (eg: "Failed to acknowledge the message").
Is there any better way to handle exceptions from a Future in a piece of code like that above?
Is there a way to avoid using a recursive function here? We did that, because we wanted to attempt delivery of the message to Kafka for like 10 seconds, before sending it as an email to look at.
Side note: I still didnt use buffer-available-bytes attribute from kafkaTemplate.metrics(), I intend to use it to minimize the chance of this problem, but still need to handle the above just in case of some race conditions
There are a few ways to do this, but I really like Spring Retry as a way to solve this kind of problem. It's a bit of pseudo code here, but if you need more specifics on how to do it, I could make things more explicit:
#Retryable(maxAttempts = 10, value = KafkaSendException.class)
public Response post(Message message) throws FailedToPostException{
ListenableFuture<SendResult<String, Message>> future = kafkaTemplate.send("topicName", message);
try {
future.get(1. TimeUnit.SECONDS);
} catch(SomeException ex) {
LOGGER.error("Message Post Failed. '{}'", ex.getCause().getMessage(), ex);
throw ex;
}
LOGGER.info("Post Finished. '{}' with offset: {}", message,
result.getRecordMetadata().offset());
}
Effectively does the same thing without recursing. I wouldn't recommend recursing code for error handling.
The controller should be able to massage the actual KafkaSendException with a nice #ExceptionHandler.

Custom timeout message in Retrofit

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"
}
}

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

RESTEasy JSON Exception Response: Send list of objects

I have web client (HTML5) and backend server based on RESTEasy webservices and session beans. In my server side code I am iterating over list of objects and per object i am executing some business logic:
List<TestTO> failedTestList = new ArrayList<TestTO>();
for (TestTO testTO : testTOList) {
try {
// some weired business logic :P
} catch (Exception e) {
logger.error("Unable to create data -" + e.getMessage());
failedTestList.add(testTO);
}
}
if (!failedTestList.isEmpty()) {
// throw custom exception embedded with failed TO list
}
I have written custome exception handlers, to catch exceptions and return proper response back to client. This class looks like:
public class CustomExceptionHandler implements ExceptionMapper<CustomException> {
public CustomException getCustomErrorCode(final CustomException customException) {
// Some logic to get cause and set error code
return customException;
}
#Override
public Response toResponse(final CustomException customException) {
return Response.serverError().entity(
"{\"Error Code\":\"" + getCustomErrorCode(customException).getErrorCode() + "\", "
+ "\"Error Message\":\"" + customException.getLocalizedMessage() + "\"}").build();
}
}
I am thinking of an option to send this failed TO list back to client, so that it can understand processing of which objects got failed. I was going through different articles, but could not find anything which fits my requirement.
Please give me an idea and link to reference, on how to implement such requirement. Please note that, my client expects response in JSON format. Please let me know, if you require more information.
Thanks.

Is this a bad practice?

Is the following code considered a bad practice? Do you think it can be done otherwise?
The goal is to always update the status, either with success (i.e invocation to service.invoke(id);returns normally ) or with failure...
#Autowired
private Service service;
public void onMessage(Message message) {
String id = null;
String status = "FAILED";
try {
id = ((TextMessage) message).getText();
status = service.invoke(id); //can throw unchecked exception
} catch (final JMSException e) {
throw new RuntimeException(e);
} finally {
if (StringUtils.isNumeric(id)) {
service.update(id, status);
}
}
}
It depends on your Use-case, whether you have to perform a step or not based on previous step. Using finally may execute your second step regardless what exception you may receive.
I would recommend having the second step outside try...catch block so that you'll update only when you have got any exception you've Expected and continue to your second step, else, your method will throw and exit.
i think you should not use implementation of message listener , you should wire them independent of spring tech . just pojo based . use <jms:listener-container > with <jms:listener>

Categories

Resources