Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Asynchronous HTTP and Comet architectures

An introduction to asynchronous, non-blocking HTTP programming

  • Print
  • Feedback

Page 5 of 7

Content streaming

The onResponse() and onRequest() methods in Listing 10 will be performed immediately after the message header is received. (The xSocket-http module also supports an InvokeOn annotation to specify if the callback method should be performed after receiving the message header or after receiving the complete message).

To reduce the required buffer sizes and to minimize call latency, the message body should be streamed. In Listing 10 this will be done implicitly by the xSocket-http module. The environment detects that an incomplete received message should be forwarded and registers a non-blocking forward handler on the incoming message-body channel.

Lower buffer sizes are required within the proxy because only parts of the message have to be buffered internally when it is transferred. Furthermore, the latency incurred by forwarding the message could be reduced significantly. If the message is forwarded in a non-streaming manner, first the whole message will be received and buffered before being forwarded. This adds the elapsed time between receiving the first and last message byte to the complete call latency.

Upload data streaming in Java Servlet API 2.5

Streaming is also supported by the current Servlet API, but only in a blocking way. When running the UploadServlet in Listing 11 in Tomcat (version 6.0.14, default configuration on Windows), the doPost() method will be called immediately after the request header is received. This allows you to stream the incoming message body. The UploadServlet reads some message-header entries and streams the message body into a file. If not enough data is available, the HttpServletRequest's input stream read() method will block. This means the request-handling thread will be suspended until more data is received.

Listing 11. Upload servlet example

class UploadServlet extends HttpServlet {
                
   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                        
      String requestURI = req.getRequestURI();

      if (requestURI.startsWith("/upload")) {
         String filename = requestURI.substring("/upload".length() + 1, requestURI.length());
         File file = new File("files" + File.separator + filename);
         file.createNewFile();

         FileOutputStream os = new FileOutputStream(file);
         InputStream is = req.getInputStream();
                                
         byte[] transferBytes = new byte[8196];
         int len;
         while ((len = is.read(transferBytes)) > 0) {
             os.write(transferBytes, 0, len);
         }
         os.close();
         is.close();
                                                                
      } else {
         resp.sendError(404);
      }
   }
}

Non-blocking upload data streaming

Outstanding threads can be avoided by using non-blocking streams. Listing 12 shows an UploadRequestHandler, which reads and transfers the incoming message body in a non-blocking way. Similar to the client-side non-blocking streaming example in Listing 6, a non-blocking body channel will be retrieved and a body-data handler will be set. After this operation the onRequest() method returns immediately, without sending a response message. If body data is received, the body-data handler will be called to transfer the available body data into a file. If the complete body is received, the response message will be sent.

Listing 12. Asynchronous, non-blocking server-side example

class UploadRequestHandler implements IHttpRequestHandler {
            
   public void onRequest(HttpRequest req, final IHttpResponseContext respCtx) throws IOException {

      String requestURI = req.getRequestURI();

      if (requestURI.startsWith("/upload")) {
         String filename = requestURI.substring("/upload".length() + 1, requestURI.length());
         final File file = new File("files" + File.separator + filename);
         file.createNewFile();
         
         final FileChannel fc = new RandomAccessFile(file, "rw").getChannel();


         IBodyDataHandler bodyToFileStreamer = new IBodyDataHandler() {

            public boolean onData(NonBlockingBodyDataSource bodyDataSource) {
               try {
                  int available = bodyDataSource.available();
     
                  if (available > 0) { 
                     bodyDataSource.transferTo(fc, available);
      
                  } else if (available == -1) {
                     fc.close();
                     respCtx.send(200);
                  }
               } catch (IOException ioe) {
                  file.delete();
                  respCtx.sendError(500);
               }
               return true;
            }

            //...
         };

         // set handler to stream the body into a file in a non-blocking manner 
         req.getNonBlockingBody().setDataHandler(bodyToFileStreamer);

      } else {
         respCtx.sendError(404);
      }        
   }
}


IServer server = new HttpServer(80, new UploadRequestHandler());
server.run();

  • Print
  • Feedback

Resources