I have jetty server, which does some logic, when it is finished, it creates a json string (using jackson) and sends it as a response. If there is an exception during the creation of the json, a JsonProcessingException is thrown. This exception bubbles up to an UncaughtErrorHandler (extends ErrorHandler in Jetty), which logs the exception and returns some failure message and status code 500.
This is just for a backend api.
The endpoint is not idempotent (it is a post endpoint), there are changes to the state of the application (ie database) when endpoint is hit and logic applied.
Now if JsonProcessingException occurs, the user will get failure message and will not know that the process/logic has been done.
How do I handle this?
Here are my thoughts on possible solutions:
Leave existing behaviour, and if user complains then tech support can clarify that the process has gone through. Or error will alert support, and they will check the logs and contact the user to say it has gone through.
Leave the endpoint as idempotent (or similar ie no change in state of the app), so that the user can send the same request (with the same body) and get a response (when it is working ie no JsonProcessingException) which states it has already done it, or cannot do it as it has already done.
Catch the JsonProcessingException when creating the json string, log it with exception message, and create a response without json informing the user that process has been done. Although this means the user will need to handle two different responses, but reduces human interaction in the above (current) solution.
OR convert/wrap it in a runtime exception (or other exception) and throw this in the catch block. But assign a better exception message (ie process was completed). Then in errorHandler, I can display this exception message in the response body, when it finds a specific exception. This way the user will know that the process was complete and not send another resquest. But as above, the user will have to handle to different types of responses.
Do not use Jackson to create the json string, do it manually using String.format() and a json template. This is fine for simple json, but complex json will be a nightmare.
Have some logic which checks if the previous call was done but not confirmed in the response, and then makes a call to the user (via some client ie email/sms) with correct details. Seems a lot of work.
Do you have any other suggestions?
Here is some example code to show where this is happening:
private String createFailedResponseBodyJson(FailedPlaneLandStatus failedPlaneLandStatus) throws JsonProcessingException {
LinkedHashMap<String, String> jsonBody = new LinkedHashMap<>();
jsonBody.put("PlaneId", failedPlaneLandStatus.planeId.value);
jsonBody.put("PlaneStatus", failedPlaneLandStatus.planeStatus.name());
jsonBody.put("AirportStatus", failedPlaneLandStatus.airportStatus.name());
jsonBody.put("LandFailureReason", failedPlaneLandStatus.failureMessage.toString());
return new ObjectMapper().setDefaultPrettyPrinter(new DefaultPrettyPrinter())
.writerWithDefaultPrettyPrinter().writeValueAsString(jsonBody);
}
It is the writeValueAsString() method which throws the JsonProcessingException
I think I am happy with the existing behaviour (First bullet point). But I just want to know if the other solutions (other bullet points) are viable, or if there is another solution?
Thanks
Related
Let's say I have endpoint returning weather from 3rd party api:
#Path("/weather")
Response getWeather(double latitude, double longtitude) {
Weather weather = weatherService.getWeather(latitude, longtitude);
return wrapWithHttpOK(weather);
}
It works perfectly fine when the 3rd party weather api always successfully returns weather. But sometimes it can return:
400 Bad request for invalid latitude, longtitude
404 Not found for locations thst this api does not cover
500 Internal server error
What is the best way to organize my code to return 400 and 404 from my api and handle 500 with 503 Service not available? Is it good idea to throw custom exceptions for this cases? Or should i make weatherService return some kind of status alongside with entity? Is there a library for this purpose?
Would also be nice to know if there is any literature or articles covering such questions.
Generally we throw exceptions rather than returning error codes.
Exceptions force the client of your code (consumer of your API) to handle the exception. For example a 4xx HTTP code could indicate faulty code.
Usually the caller expects and depends on the result of your API. When your API fails to satisfy this expectation it must communicate this exceptional situation or object state by throwing an exception.
Error codes are used accross application boundaries, like when consuming a Rest service. The remote service can't throw an exception in the client application. It can only send you a response containing a special code or a special response object to indicate an error.
As soon as such an error enters your application/library, you must convert it to a meaningful exception which means a meaningful exception name and a meaningful exception message. Consider to extend Exception if there is no meaningful standard exception available.
A good exception name and message is far more convenient to handle than a code.
While the name and the message can be directly consumed, a code must always be decoded e.g., by using an error code table.
Since exceptions are expensive in terms of performance, you should always avoid them in the first place. It is primarily the responsibility of the caller to ensure that the arguments passed to the API are valid.
Then have your API to validate the parameters before using them (e.g., with the "expensive" Rest service - maybe you don't whant to waste bandwidth by sending invalid requests) and throw ArgumentException if they are invalid.
In addition you can design your API in way that it semantically communicates that a failing operation is possible by adding an alternative the Try-Do pattern implementation:
public boolean tryGetWeather(Response response) {
// Call Rest API
if (restApiResponseCode != 200) {
return false;
}
// Set the result parameter
response.Weather = weatherData;
return true;
}
The tryxxx implies that the operation is expected to fail and that the ccaller should observe and handle the result. This is usually used for non-critical operations (e.g., value conversions - like testing if a string is a number).
When using the Try-Do pattern, a good API always offers a breaking default version in case the consumer wants the application to halt on error (because it may indicate faulty code):
public Response getWeather(double latitude, double longtitude) {
// Validate parameters before using them.
// Longitudes range from -180 to 180.
if (longitude > 180 || longitude < -180)
throw new InvalidLongitudeException();
// Call Rest API
if (restApiResponseCode != 200) {
throw new WeatherServiceException();
}
// Set the result parameter
response.Weather = weatherData;
return response;
}
Since you would always expect a weather result from a weather API, the Try-Do pattern is not suitable for your scenario. You should convert the error codes to exceptions.
Wether you use the Try-Do pattern for less critical operations or not: always throw exceptions rather than returning error codes.
The code base I am working with is spring and jersey.
I am on this new code base, and within the 500 exceptions got back from REST call in response.context.entityContent, I found that the RO containing the true exception is a ByteArrayInputStream.
Right now I can extract the server exception message with ex.getResponse().readEntity(CustomRO.class).getMessage().
The 500 exceptions containing the RO, however, does not contain the cause (ex.getCause() = null).
Is there a way I can apply read Entity to all exceptions on the client side, get the exception in the RO and put it as the cause of the 500 InternalServerErrorException?
Please correct me if I am wrong in any of my thoughts, and let me know if there is any standard that I should follow.
Also it is my first time asking here, so I am sorry if my wording is long or not to the point
I've made a custom spring HttpMessageConverter, in order to create and output csv from a rest controller.
I.e. the converter is registered to handle the class type that is returned from the controller.
I use the supercsv library/framework to generate the actual csv, and therefore have a CsvMapWriter using the stream from the body of the HttpOutputMessage provided in the writeInternal overwritten method:
protected void writeInternal(MyType myObject, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
OutputStreamWriter oswriter = new OutputStreamWriter(outputMessage.getBody());
ICsvMapWriter mapWriter = new CsvMapWriter(oswriter,CsvPreference.STANDARD_PREFERENCE);
I then go on to use the mapWriter to write the csv lines directly to the stream, e.g.
try{
mapWriter.writeHeader(transactionHeader);
mapWriter.write(transactionLevelMap, transactionHeader, getTransactionInfoProcessors());
} finally {
if (mapWriter != null) {
mapWriter.close();
}
}
Since the writing - and csv map processing - can throw exceptions, I wrap the code with a try-finally, as a "standard approach" when dealing with resources like streams.
The problem is, that when the thrown exception bubbles up through the call chain, and is being picked up by the spring error handling mechanism - that wants to return a http status 500 to the client, indicating a server error, I get the following dump:
WARN 3208 --- [ qtp32193376-20] org.eclipse.jetty.server.HttpChannel : Could not send response error 500
I assume it is due to the fact, that I've just closed the stream from the HttpOutputMessage, which Spring now want to use for writing the error status etc.
That makes sense, but my question now is:
Should i NOT close the stream in the "finally" clause, when an exception is being thrown? (And only close it, when I now that all processing went well)
I.e. can I assume that Spring will take care of the resource deallocation once it is done with it, when it uses it for the error handling?
I am trying to implement a process consisting of several webservice-calls, initiated by a JMS-message read by Spring-integration. Since there are no transactions across these WS-calls, I would like to keep track of how far my process has gone, so that steps that are already carried out are skipped when retrying message processing.
Example steps:
Retrieve A (get A.id)
Create new B for A (using A.id, getting B.id)
Create new C for B (using B.id, getting C.id)
Now, if the first attempt fails in step 3, I already have a created a B, and know it's id. So if I want to retry the message, it will skip the second step, and not leave me with an incomplete B.
So, to the question: Is it possible to decorate a JMS-message read by Spring-integration with additional header properties upon message processing failures? If so, how could I do this?
The way it works at the moment:
Message is read
Some exception is thrown
Message processing halts, and ActiveMQ places the message on DLQ
How I would like it to work:
Message is read
Some exception is thrown
The exception is handled, with the result of this handling being an extra header property added to the original message
ActiveMQ places the message on DLQ
One thing that might achieve this is the following:
Read the message
Start processing, wrapped in try-catch
On exception, get the extra information from the exception, create a new message based on the original one, add extra info to header and send it directly to the DLQ
Swallow the exception so the original message dissappears
This feels kinda hackish though, hopefully there is a more elegant solution.
It's hard to generalize without more information about your flow(s) but you could consider adding a custom request handler advice to decorate and/or re-route failed messages. See Adding Behavior to Endpoints.
As the other answer says, you can't modify the message but you can build a new one from it.
EDIT:
So, to the question: Is it possible to decorate a JMS-message read by Spring-integration with additional header properties upon message processing failures? If so, how could I do this?
Ahhh... now I think I know what you are asking; no, you can't "decorate" the existing message; you can republish it with additional headers instead of throwing an exception.
You can republish in the advice, or in the error flow.
It might seem like a "hack" to you, but the JMS API provides no mechanism to do what you want.
From the spring forum:
To place new header to the MessageHeaders you should use
MessageBuilder, because not only headers, but entire Message is
immutable.
return MessageBuilder.fromMessage(message).setHeader(updateflag, message.getHeaders().get("Lgg_Rid") == "ACK" ? "CONF" : "FAIL").build();
In an asynchronous context, errors will go to an error channel - either one you configure yourself and indicate in the message headers with errorChannel, or a global error channel if none is specified. See for more details here.
I've already looked for the answer for this question, and I've found the following suggestions:
If you are always expecting to find a value then throw the exception if it is missing. The exception would mean that there was a problem. If the value can be missing or present and both are valid for the application logic then return a null.
Only throw an exception if it is truly an error. If it is expected behavior for the object to not exist, return the null.
But how should I interpret them in my (so casual) case:
My web app controller is receiving request to show details for a user with a certain id. Controller asks the service layer to get the user, and then the service returns the object, if it's found. If not, a redirect to 'default' location is issued.
What should I do when someone passes invalid user id inside the request URL? Should I consider it as "expected behaviour" and return null to the controller, or perhaps should I call it a "problem or unexpected behaviour" and thus throw an exception inside the service method and catch in inside the controller?
Technically it's not a big difference after all, but I'd like to do it the right way by following standard convetions. Thanks in advance for any suggestions.
EDIT:
I assume, that the URLs generated by the app are valid and existing - when clicked by user, the user with a certaing id should be found. I want to know how to handle a situation, when user tries to access URL with wrong (not existing) user id, by manually typing the URL into browser's address bar.
If I understand you correctly, the request containing the user ID is coming from a client (out of your control). Applying the rules of thumb you quoted: invalid user input is an entirely expectable case, which would not require an exception, rather handle the null value gracefully by returning an appropriate error message to the client.
(OTOH if the user id in the request were automatically generated by another app / coming from DB etc, an invalid user ID would be unexpected, thus an exception would be appropriate.)
My personal suggestion would be to log the error details (IP address, the invalid user ID) and re-direct the user to an error page which says that some error has occurred and the administrators have been notified. Click on so-n-so link to go back to your home page etc.
Point being, whether you throw exception or return null, just make sure that the outermost filter or handler "logs" the details before the response is returned to the user.
What should I do when someone passes invalid user id inside the request URL?
You have two choices: show the 'default' page you mentioned or return a "Not found" / 404.
Regarding null, it depends. If you consider null unacceptable for a reference, then annotate it with #NotNull and the annotation shall take care of doing the correct thing upon getting a null reference: that is, throwing an (unchecked) exception (of course you need to work with the amazing #NotNull annotation for this to work).
What your do higher up the chain is up to you: to me returning a 404 to someone trying to fake user IDs sounds really close to optimal.