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?
Related
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
When having declared a method like this using Spring AMQP:
#RabbitListener(..)
public void myMethod(#Header(AmqpHeaders.CHANNEL) Channel channel, #Header(AmqpHeaders.DELIVERY_TAG) Long tag, ...)
and using manual acknowledge mode, how should one properly deal with the IOException that may be thrown when doing ACK:
try {
channel.basicAck(tag, false);
} catch (IOException e) {
// What to do here?
}
Should the exception be rethrown? Should the "basicAck" operation be retried? What's the proper way to handle it?
The standard way of doing this is using retry mechanism & to come out if none of them succeeds.
However, based on my experience, if channel throws an exception, it more or less means the channel is useless & you might have to redo the whole thing again. I normally log the error along with the required details so that I can track which message processing failed so that I can verify the same later to see if its processed or I need to do anything about it.
How can I detect that the client side of a tomcat servlet request has disconnected? I've read that I should do a response.getOutputStream().print(), then a response.getOutputStream().flush() and catch an IOException, but is there a way I can detect this without writing any data?
EDIT:
The servlet sends out a data stream that doesn't necessarily end, but doesn't necessarily have any data flowing through it (it's a stream of real time events). I need to actually detect when the client disconnects because I have some cleanup I have to do at that point (resources to release, etcetera). If I have the HttpServletRequest available, will trying to read from that throw an IOException if the client disconnects?
is there a way I can detect this
without writing any data?
No because there isn't a way in TCP/IP to detect it without writing any data.
Don't worry about it. Just complete the request actions and write the response. If the client has disappeared, that will cause an IOException: connection reset, which will be thrown into the servlet container. Nothing you have to do about that.
I need to actually detect when the client disconnects because I have some cleanup I have to do at that point (resources to release, etcetera).
There the finally block is for. It will be executed regardless of the outcome. E.g.
OutputStream output = null;
try {
output = response.getOutputStream();
// ...
output.flush();
// ...
} finally {
// Do your cleanup here.
}
If I have the HttpServletRequest available, will trying to read from that throw an IOException if the client disconnects?
Depends on how you're reading from it and how much of request body is already in server memory. In case of normal form encoded requests, whenever you call getParameter() beforehand, it will usually be fully parsed and stored in server memory. Calling the getInputStream() won't be useful at all. Better do it on the response instead.
Have you tried to flush the buffer of the response:
response.flushBuffer();
Seems to throw an IOException when the client disconnected.
How can I detect that the client side of a tomcat servlet request has disconnected? I've read that I should do a response.getOutputStream().print(), then a response.getOutputStream().flush() and catch an IOException, but is there a way I can detect this without writing any data?
EDIT:
The servlet sends out a data stream that doesn't necessarily end, but doesn't necessarily have any data flowing through it (it's a stream of real time events). I need to actually detect when the client disconnects because I have some cleanup I have to do at that point (resources to release, etcetera). If I have the HttpServletRequest available, will trying to read from that throw an IOException if the client disconnects?
is there a way I can detect this
without writing any data?
No because there isn't a way in TCP/IP to detect it without writing any data.
Don't worry about it. Just complete the request actions and write the response. If the client has disappeared, that will cause an IOException: connection reset, which will be thrown into the servlet container. Nothing you have to do about that.
I need to actually detect when the client disconnects because I have some cleanup I have to do at that point (resources to release, etcetera).
There the finally block is for. It will be executed regardless of the outcome. E.g.
OutputStream output = null;
try {
output = response.getOutputStream();
// ...
output.flush();
// ...
} finally {
// Do your cleanup here.
}
If I have the HttpServletRequest available, will trying to read from that throw an IOException if the client disconnects?
Depends on how you're reading from it and how much of request body is already in server memory. In case of normal form encoded requests, whenever you call getParameter() beforehand, it will usually be fully parsed and stored in server memory. Calling the getInputStream() won't be useful at all. Better do it on the response instead.
Have you tried to flush the buffer of the response:
response.flushBuffer();
Seems to throw an IOException when the client disconnected.
I'm working on a Thrift server which is basically just a wrapper around the Stanford Parser (although that's not too important). Sometimes the Stanford Parser will throw useful exceptions depending on the input it's given; for instance, if the input is too long (according to the parser), the user generating the input should receive this exception so they can decide how to handle it. However, I can't seem to get Thrift to pass this exception up, and instead only returns
Internal error processing <name of Thrift method being called>
to the client.
I have the following code in that method:
try
{
// a whole bunch of Stanford Parser stuff
}
catch (Exception e)
{
throw new TApplicationException(TApplicationException.INTERNAL_ERROR, e.getMessage());
}
and the method does throw a TApplicationException, but whatever the contents of e.getMessage() are are not being sent to the client. How can I get the exceptions being thrown by the Stanford Parser to be thrown by Thrift to the client?
I am afraid you have to define your own exceptions, instead of using TException or any subclass of it.
That's because Thrift framework wrap your code like this(ProcessFunction.java):
try {
result = getResult(iface, args);
} catch(TException tex) {
LOGGER.error("Internal error processing " + getMethodName(), tex);
TApplicationException x = new TApplicationException(TApplicationException.INTERNAL_ERROR,
"Internal error processing " + getMethodName());
oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.EXCEPTION, seqid));
x.write(oprot);
oprot.writeMessageEnd();
oprot.getTransport().flush();
return;
}
So, whatever message you give in TException, will be ignored and replaced by Thrift framework.