I know the title is a bit confusing. I will describe my task:
Now I'm writing a webpage, which allow me to download some data or do some other stuff. The front end is jsp and the back end is java. So every time when I do something on this page(e.g. sort the data or download the data), a request will be sent to java and after data processing the .jsp page will be refreshed and loaded again(of cause after sorting it should be refreshed). But I just found that if I download a data with the following code, the refreshing will be interrupted:
OutputStream out = response.getOutputStream(); // response is HttpServletResponse
doSomeOutput(out); // Just write something into OutputStream
out.flush(); // **PAY ATTENTION**. The refreshing is interrupted here!
out.close();
When I was debugging, the page was keeping loading until the third line. And then the page was not refreshed and the data was download.
I hope you can understand what I'm talking about:(
So now I have 2 questions: 1. How can I let the process keep working after out.flush();? 2. In my task I need the same behavior(the process is interrupted) in some other modules, but there's nothing to be download. So how can I interrupt the process besides out.flush();?
Thank you guys!!
1)
https://docs.oracle.com/javaee/5/api/javax/servlet/ServletResponse.html#getOutputStream%28%29
"Calling flush() on the ServletOutputStream commits the response"
as i understand this: active refreshing is stopped, as we got main part of data. But you would be able to send data even after commit. So you can keep working after flush.
Set headers are also sent with flush.
2) Pls explain more thoroughly. call "return", or close?
Related
i'm trying to create an asynchronous servlet, so that the client makes a request, get an answer like "process started" and stops waiting.
The servlet will start some computation and write the result on a DB and then the user will be able to check if the process is terminated and to see its result.
Well, every time i try to do this a get errors. In the request i have a Connection object, and if i pass it to a Thread and then start it, when the main servlet ends the connection gets closed and the Thread throws an Exception ('connection has already been closed').
Is there a way to just give to the client a response and tell it not to wait anymore while the servlet does all its job?
Thank you in advance!
If you were mentioning a DB Connection, why not create the Connection inner your Thread?
There exists Ajax (JavaScript) in the browser to place a call, and have success/failed callbacks received later. That would be the most regular way.
If more is required, register the request for scheduling in a queue = store something in the database. Then have a timer task (for instance from Spring) in your app that does the work. This prevents overloading, DoS.
In fact if looks like the response is fully sent to the client when you close the response output stream (or writer), and you can continue the processing inside the servlet without any threading issues. Unfortunately I could never found a confirmation that if was explicitely allowed in servlet specs, but I could do it successfully in Tomcat (was using version 7)
So provided you note it in a bold comment, your servlet code could look like:
void service(ServletRequest req, ServletResponse resp) {
OutputStream out = res.getOutputStream();
... // generate the output
out.close(); // the response is sent to client
// asynchonous processing
...
}
Im trying to wrap my head around Java Out/Inputstreams, closing and flushing. I have a situation where I want to create a file using Apache POI with data from a server. I would like the file to start downloading as soon as I retrieve the first record from the DB(Show the file at the bottom of the browser has started to download).
public void createExcelFile(final HttpServletResponse response,
final Long vendorId) {
try {
// setup responses types...
final XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
final XSSFSheet sheet = xssfWorkbook.createSheet("sheets1");
// create file with data
writeExcelOutputData(sheet, xssfWorkbook);
xssfWorkbook.write(response.getOutputStream());
xssfWorkbook.close();
}
catch (final Exception e) {
LOGGER.error("Boom");
}
The above code will perform a file download no problem, but this could be a big file. I go off getting the data(around 20/30s) and then after that the download begins < no good...
Can I achive what I need or whats the best approach, thanks in advance
Cheers :)
Reasons could be as following:
maybe there is a read/write timeout with your http server, then if the process gets lengthy or becasue of low-bandwidth, so the connection will be closed by the server.
make sure the process(the excel work) gets completely done, maybe there would be an error/exception during work.
The solution of Jorge looks very promising. User need once request for a file, then server would do the work in background and then either user check the work process and download the file if ready, or server informs the user by email, web notification, etc...
Also you would keep the file in the server in a temp file for a while, and if the connection gets interrupted, server would respond the generated file partial(omit the bytes sent, like normal file download)
Keeping a connection alive to do a lengthy work is not very logical.
Again, if the file gets ready fast(really fast) for download/stream, and the download interrupts, if could be becasue of read/write timeout by server, or a very bad network.
I'm reading an http response using the apache async client. Every time I read a chunk of data I want to write it to the servletoutputstream in a non-blocking mode. Something like this:
// decoder.read is executed when data available for reading
while (decoder.read(this.bbuf) > 0)
{
this.bbuf.flip();
arr = new byte[numbytesread];
this.bbuf.rewind();
this.bbuf.get(arr);
// Blocking write to servletoutputstream 'sos'
this.sos.write(arr);
this.bbuf.compact();
}
Obviously this does not work even if I wrap the 'sos' in a WritableChannel, because I always end up with the bytearrayoutputstream from the servletoutputstream.
So I should add a WriteListener to the servletoutputstream to switch to nio mode, but here comes the problem I'm not able to solve. How can I pass every chunk of data from my http callback to the writelistener to work asynchronus and non blocking?
Is this possible? If so, can anyone give me a clue about how to do it?
Currently, i using XmlHttpRequest to uploading files to the server using HTML5 capabilities.
There's progress bar:
xhr.upload.addEventListener('progress', function(e) {
var done = e.position || e.loaded, total = e.totalSize || e.total;
console.log(done);
});
... everything works fine, but it doesn't consider processing the file by server. So it shows 100% uploaded even when file weren't created yet.
The file receiver is Java servlet, which able to response only after return. So here's no way to count the percents left by its response.
Whether there are ways around it?
If the processing the server does takes a long time and you want to give feedback while it happens, here's a rough outline of a solution. This will take a while to implement so it's only really worth doing if the processing is slow.
Modify your servlet to do its work asynchronously, and return a 201 Accepted response to the client.
As the server processes the file, set the progress on an object, typically a ConcurrentHashMap injected with Spring if that's what you're using.
Expose an API that queries the current progress of the task without blocking for it to complete.
In your javascript, poll this API until the task completes, and show the progress.
You can return a tracking ID in the response at step 1 if you need to.
I have web service method that is supposed to process a very large file and output several files to the server. However, this web service will just timeout and there will be no way for the invoker to get the CREATED status. I am just wondering whether there is a way to run the processing job (starting a new thread or something) and return the status without waiting for the process to be done.
public Response processFile(InputStream inputStream){
//I want to process the file here
//but I dont want the invoker to wait for that process to finish
//I just want the response to be returned right away
return Response.status(Response.Status.CREATED).build();
}
The file comes from the input stream, right? So if you'll send back a CREATED status (effectually closing the connection) you might sever the connection before you receive the entirety of the input file?
That's what i thought anyways... In which case you'll just want to set the timeout to a lengthier value.
If that's not the case, then i guess it would be fine to start a new thread, process everything there in good time and send back the CREATED status.