Best way to handle badly written external exception? - java

I have an external service I'm calling that just returns 500's with the SAME exception each time no matter the issue.
For example(my api to their service):
Action: Fetch image that does not exist
IMGException: Status code: 500, ErrMsg: File not found
Action: Fetch image that does exist but there are server side issues
IMGException: Status code: 500, ErrMsg: Cannot grab img at this time
Action: Fetch image that does exist but is expired
IMGException: Status code: 500, ErrMsg: Img is expired
What would be the best way to handle this? I was catching them and giving them more descriptive messages to throw to my #ExceptionHandler for logging, etc. Should I just throw them and never catch them since I cannot dependably predict what the exception is caused from and therefore cannot correctly change the status code or message?

You can parse the ErrMsg and throw your own exceptions. But Since the response is from an external service, you can as well include the message from external service in the response to your API in a separate field like ExternalMessage.
This will help the users in case the response from external API changes and you end up throwing a different exception.

I recommend you to simply rethrow these exceptions with the information that the server sends to you and add any information you have. But do not add new information based on what you recieved (with if's, for example), because if they change something your code will just be deprecated.
Of course, never show crypt messages to your final user. In this case add some generic message with instructions about what they can do.

Related

Asana Events autosync

I want check events and change sync parameter in Asana Api in java. By Asana docs (https://developers.asana.com/docs/events). We have:
Client client = Client.accessToken("PERSONAL_ACCESS_TOKEN");
List<JsonElement> result = client.events.getEvents(sync, resource)
.option("pretty", true)
.execute();
In java I have error and don't see new "sync" parameter:
com.asana.errors.InvalidTokenError: Sync token invalid or too old
(Sync token invalid or too old. If you are attempting to keep
resources in sync, you must fetch the full dataset for this query now
and use the new sync token for the next sync.)
In Asana API explorer (https://developers.asana.com/explorer) I see this:
GET /events?resource=1164252
{
"errors": [
{
"message": "Sync token invalid or too old. If you are attempting to keep resources in sync, you must fetch the full dataset for this query now and use the new sync token for the next sync."
}
],
"sync": "c66c5705fb8286666f944f8e314a82c6:0"
}
So in Java I take error message, but not a parameter "sync". If use try / catch (catch (IOException ex)) system, then I can see "sync", that I need
Picture of ex with breakpoint, but how to get this info from Exception object (it's not main message from exception)?
Maybe somebody work with Asana library and know this problem (how to copy sync parameter first time and later from server response)?

Correct way to handle a REST API path that can be called synchronously or asynchronously

I'm working on a Spring Boot REST API that handles document and can launch a check on a document.
I have a document resource: /doc:
Create a doc with POST /doc
Rest of the CRUD actions with /doc/{id}
Now I can launch a check on a doc, check can be seen either as an action or as a sub-resource.
It's pretty straightforward to launch (create) a check on a document: POST /doc/{id}/check
The check can however take some time so I want to give the user the choice to launch a synchronous or asynchronous check.
How would I handle this path wise?
Should the user choose sync or async check through a query parameter on POST /doc/{id}/check?
Should I create 2 separate paths?
Also in the case of an async check, I would create a temporary Task resource that can be pooled to know the status of the check.
But then if both check and task are returned from the same path it gets confusing, no?
I read an article that says the resource returned in async should be a check resource filled as much as possible but with a link to the task that can be pooled.
That seems like a good way; I would return a partial check if async with a link to the /task/{id} associated with the check.
However I'm still confused as to what path my API should offer to let the user pick between sync and async checks.
How would you handle it path and resource wise?
Basically it's up to you. Usually if it's a big chunk of data you want to query like /resource/{id} most APIs I have used use GET for synchronous requests and POST for async request returning task or job ID.
For POST in your case if the creation/checking takes time I would consider always doing it asynchronous and returning HTTP 202 Accepted and doc/{id}/check/{id} url where the user can see the result if it is ready or some status that it is still working.
If you want to give them a choice to wait or not it's up to you how to do it. There is a standard header that can be used to modify behavior. For example Expect: 202-accepted for async calls and no header or Expect: 201-created for synchronous calls. This makes the API a bit less clear even though it is a standard. Most people (including me) would probably stick to adding a parameter to the URL for clarification. I don't think it should be in the POST data because it should be data related to the object you are creating
There are multiple questions here. I would try to answer one by one
Checking the health of a resource can be done with query param
/doc/{id} - GET Get the resource details
/doc/{id}?healthCheck=true&async=true GET - Get the resource details and trigger an async health check
For the async health check the response as you mentioned will be 202 and the response contains the link to the health status URL
HTTP/1.1 202 Accepted
Location: /doc/12345/status
If the client sends a GET request to this endpoint, the response should contain the current status of the request. Optionally, it could also include an estimated time to completion or a link to cancel the operation.
Reference
https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design

Catch a specific Elasticsearch exception from a BulkRequest

I use Java to index some documents with a BulkRequestinto Elasticsearch 1.4.2.
Some of these docs only need to be written when they are not already in the index, so I set the CREATE-opType like this:
indexRequestBuilder.opType(IndexRequest.OpType.CREATE)
Now the docs which were already in the index fail in the BulkResponse.
Error message bulkItemResponse.getFailureMessage():
DocumentAlreadyExistsException[...]
I want to ignore this class of exception but retry writing the docs for all other type of exceptions.
So how can I catch just the DocumentAlreadyExistsException?
I can get the Failure with bulkItemResponse.getFailure(), but I cannot find any information about the type of the Exception beside the error message.
I could look in the error-message for the exception name, but this may be rather fragile with new Elasticsearch versions:
if(bulkItemResponse.getFailureMessage().startsWith("DocumentAlreadyExistsException[")
Is there a better way?
This cant be possible. The bulk request is actaully executed on the server side and not client side. And hence all it can do is to sent the stacktrace back and not the Exception object.

RuntimeException does not halt GAE but returns HTTP 500 when run locally

Let me first provide some background information. If you don't care you can skip to the next paragraph. I wanted to use the DrEdit sample Java application which is integrated with Google Drive as the basis of my application. I need to refactor the code, though, because the original scenario assumed that the user would only access the application through the Drive and never directly. Currently when the latter happens, a RuntimeException is thrown which should not be the case in a normal flow.
Thanks to that issue I stumbled upon a difference between my local environment and the GAE which is manifested when the following code is run:
} catch (CredentialMediator.NoRefreshTokenException e) {
try {
resp.sendRedirect(e.getAuthorizationUrl());
} catch (IOException ioe) {
throw new RuntimeException("Failed to redirect user for authorization");
}
throw new RuntimeException("No refresh token found. Re-authorizing.");
}
When I run this application on GAE, the RuntimeException is thrown (I can see it in the logs) and the sendRedirect is also executed so I get to see the page that should be displayed.
However when I run the same application locally, I get the HTTP 500 error and the RuntimeException is displayed but the sendRedirect is ignored.
So far I haven't been successful in finding an explanation for this behaviour. I would like to know why this is the case and if there are settings that I can change in order to fully replicate the GAE environment locally.
This is how standard defines the sendRedirect(). It actually commits the response so after calling this method you should not be able to change or add to the response. However it does not define what happens if you trigger an exception after redirect.
Anyway, your code is ambiguous on purpose - you should not continue processing the request and throw exceptions after sending redirect. If you have any processing to do, then do it before redirect.
OTOH you should not rely on generic exception handling. Instead install a servlet filter that catches exceptions and return a proper user-readable or device-readable response.

Work around for MessageNotReadableException in Java

I am building a small api around the JMS API for a project of mine. Essentially, we are building code that will handle the connection logic, and will simplify publishing messages by providing a method like Client.send(String message).
One of the ideas being discussed right now is that we provide a means for the users to attach interceptors to this client. We will apply the interceptors after preparing the JMS message and before publishing it.
For example, if we want to timestamp a message and wrote an interceptor for that, then this is how we would apply that
...some code ...
Message message = session.createMessage()
..do all the current processing on the message and set the body
for(interceptor:listOfInterceptors){
interceptor.apply(message)
}
One of the intrerceptors we though of was to compress the message body. But when we try to read the body of the message in the interceptor, we are getting a MessageNotReadableException. In the past, I normally compressed the content before setting it as the body of the message - so never had to worry about this exception.
Is there any way of getting around this exception?
It looks like your JMS client attempts to read a write-only message. Your interceptor cannot work this way, please elaborate how you were compressing message earlier.

Categories