Enol Pertierra
Enol Pertierra

Reputation: 51

When response is delivered to client in asynchronous Servlets?

I'm having problems understanding how asynchronous servlets work, and in general how servlets deliver their response to the client.
What I'm trying to do is upload a video to a servlet via ajax. I thought that using an async servlet, I would obtain the response immediately in my browser and then the long task would be done in another thread.

I post my initial code here, before any code is written for file process, just an initial servlet to test asynchronism.

@WebServlet(name = "VideoUploader", urlPatterns = {"/VideoUploader"}, 
    asyncSupported = true)  

@MultipartConfig
public class VideoUploader extends HttpServlet {

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    doPost(request, response);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    final PrintWriter pw = response.getWriter();

    final AsyncContext ac = request.startAsync();
    ac.setTimeout(80000);

    ac.addListener(new AsyncListener() {
        @Override
        public void onComplete(AsyncEvent event) throws IOException {
            System.out.println("On complete");
        }

        @Override
        public void onTimeout(AsyncEvent event) throws IOException {
            System.out.println("On timeout");
        }

        @Override
        public void onError(AsyncEvent event) throws IOException {
            System.out.println("On error");
        }

        @Override
        public void onStartAsync(AsyncEvent event) throws IOException {
            System.out.println("On start async");
        }
    });

    ac.start(new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i <= 10; i++) {
                System.out.println("Async task: "
                        + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
            }
            ac.complete();
        }
    });

    pw.write("end");
    pw.close();
}
}

Then, the client part is:

<form id="formVideo">
        <label for="videoFile">Vídeo:</label>
        <input id="videoFile" name="videoFile" type="file" /> <br/>
        <input id="uploadVideoBtn" type="button" value="Subir" onClick="uploadVideo();"/>
    </form>

    <div id="notificaciones"/>            

    <script type="text/javascript">
        function uploadVideo() {
            var file = document.getElementById("videoFile").files[0];

            var formdata = new FormData();
            formdata.append("file", file);                  

            var xhr = new XMLHttpRequest();

            xhr.open("POST","/webapp/VideoUploader", true);
            xhr.send(formdata);

            xhr.onload = function(e) {
                if (this.status == 200) {
                   alert(this.responseText);
                }
            };                  
        }               
    </script>        

When I didn't attach a video to the file input, the process is done as I expected, the response is immediately received in the browser. But when I attached a file of any size, my browser doesn't receive the response until the other thread is over.

I was researching on non blocking IO, but I'm not sure if it has something to do with this behaviour or not.

I'm still not sure how I want to implement this, although I'll listen to any advice, but what I would like is to understand the behaviour of this asynchronous servlets.

Upvotes: 2

Views: 7252

Answers (2)

Lan
Lan

Reputation: 6660

Asynchronous servlet hands over the long running server side job to a different server thread. Non-Blocking IO, a new feature in servlet 3.1, deals with situation when incoming data is blocking or streamed slower than the server can read. Both are solutions to avoid servlet thread starvation. They are not about returning response to client immediately.

Since you are using Ajax, not a regular browser file upload, it should be easily implemented at the Ajax side with even a synchronous servlet, if you do not care about servlet thread starvation. Ajax is asynchronous in nature. Here is an example tutorial

http://www.javabeat.net/asynchronous-file-upload-using-ajax-jquery-progress-bar-and-java/

Upvotes: 1

Mani
Mani

Reputation: 3364

it is obivious, your browser will wait until the other thread completes.

The following steps involved

  1. Client Sent Request to Server
  2. Server allocates Thread (Servlet Container) from ThreadPool
  3. Servlet container creates Servlet instance / reuse existisng Servlet instance and invoke Servcie method in (Servlet Thread)
  4. With in Service method by calling startAsync() will start new thread and pass the request,response instances to the new Thread to process the request
    note** New Thread is not blocking the http connection , it is just a thread in the jvm which is not bliocking any IO at this moment
  5. Servlet Thread exists service method and returned to thread pool
    Note** here Response not yet sent to Client / Browser
  6. Once the Process started in step 4 completed that thread will request Servlet Container to allocate to new Servlet thread to send the respond back to Client.

Only the at Step 6 the response will return back to Client. So there is no difference between the normal request and with "asyncSupported = true" from client point of view.

Servlet 3.0 supports Threads per request by using "asyncSupported = true" instead of Thread per connection.
Thread per connection will cause Thread Starvation.

@WebServlet(name = "VideoUploader", urlPatterns = { "/VideoUploader" }, asyncSupported = true)
@MultipartConfig
public class VideoUploader extends HttpServlet {

@Override
protected void doGet(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
    doPost(request, response);
}

@Override
protected void doPost(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {

    final AsyncContext ac = request.startAsync();
    ac.setTimeout(80000);

    ac.addListener(new AsyncListener() {

        public void onComplete(AsyncEvent event) throws IOException {
            System.out.println("On complete");
        }

        public void onTimeout(AsyncEvent event) throws IOException {
            System.out.println("On timeout");
        }

        public void onError(AsyncEvent event) throws IOException {
            System.out.println("On error");

        }

        public void onStartAsync(AsyncEvent event) throws IOException {
            System.out.println("On start async");

        }

    });

    ac.start(new Runnable() {

        public void run() {
            System.out.println("Async task: "
                    + Thread.currentThread().getName());
            try {
                for (Part part : ((HttpServletRequest) ac.getRequest())
                        .getParts()) {
                    System.out.println("File received"); // You Should write
                                                            // file here
                                                            // like
                                                            // part.write("fileName");
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            } catch (ServletException e1) {
                e1.printStackTrace();
            }
            ac.complete();
            PrintWriter pw = null;
            try {
                pw = ac.getResponse().getWriter();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            pw.write("end");
            pw.close();
        }

    });

}
}

Upvotes: 2

Related Questions