RSS feed
<< JSR-315 Needs YOU! | Home

Patterns for Servlet 3.0 suspend usage.


As I have previously blogged, asynchronous coding is hard! The suspend proposal for Servlet 3.0 does take a lot of the pain out of asynchronous programming, but not all.  It has been pointed out, that my own async examples make some assumptions that simplify the code. Specifically they assume that there are no upstream suspenders (eg a filter deployed in front) that have already suspended and resumed and thus affected the values returned by isInitial, isResumed and isTimeout.

So these examples need to be a little more complex to deal with all circumstances. One way to deal with such complexity is with patterns, which can help explain the generic cases, provide a template for specific implementations and/or be the basis of frameworks to help developers.   Thus I have captured the key usages of the suspend API in the following 5 patterns:
 

Suspend/Complete Servlet

This is the simplest pattern, where a servlet suspends a request and organizes for the response to be completed by asynchronous threads or call backs. This is not affected by any upstream suspenders
  public void doGet(HttpServletRequest request, 
HttpServletResponse response)
{
request.suspend();
// arrange for response to be completed
// by async thread(s) or callback(s)
}

Simple Suspend/Resume Servlet

If a servlet developer knows that the servlet will not be fronted by suspending filters, then it can use a simplified pattern:
  public void doGet(HttpServletRequest request, 
HttpServletResponse response)
{
if(request.isInitial())
{
// handle intial dispatch
 request.suspend();
// arrange async thread/callback
return;
}

if(request.isTimeout())
{
// handle timeout
}

// generate response
}
Note that the suspend call should happen before arranging async thread/callback so that there is not a risk of a resume before the suspend.
 

Suspend/Resume Servlet

If a suspending servlet can be downstream of a filter (or dispatching servlet) that also suspends, then the isInitial(), isTimeout() and isResumed() methods may not be set due to this servlets suspend. A request attribute is required to flag that this servlet has performed the suspend. The attribute name needs to be chosen so that it will not clash with other instances. The attribute value may be a simple boolean or a more complex state object to pass information from the initial to he resume/timeout handling.
  public void doGet(HttpServletRequest request, 
HttpServletResponse response)
{
if(request.isInitial()
|| request.getAttribute("com.acme.suspend")==null)
{
// handle intial dispatch
 request.setAttribute("com.acme.suspend",
Boolean.TRUE);
request.suspend();
// arrange callback
return;
}

Boolean suspended=request.getAttribute("com.acme.suspend");
if (suspended)
{
request.setAttribute("com.acme.suspend",Boolean.FALSE);

if(request.isTimeout())
{
// handle timeout
return;
}

// handle resume
}

// generate response
}
The isInitial() call is still used as an efficiency.  If it is is true, then the request is initial for all filters and servlets.  The value of the attribute only needs to be checked if initial returns false.

Simple Suspend/Resume Filter

If a filter developer knows that there are no upstream or downstream suspenders, then a simplified pattern similar to the Simpler Suspend/Resume Servlet may be used:
  public void doFilter(ServletRequest request, 
ServletResponse response,
FilterChain chain)
{

if(request.isInitial())
{
// handle intial dispatch
 request.suspend();
// arrange async callback
return;
}

if(request.isTimeout())
{
// handle timeout
return;
}
// handle resume
chain.doFilter(request,response);
}

Suspend/Resume Filter

If a suspending filter is to be deployed where there may be either/both upstream and/or downstream suspending components, then a request attribute needs to be used to track both the initial handling and to signal that the resume/timeout has been handled. The attribute value may be a simple boolean or a more complex state object to pass information from the initial to the resume/timeout handling.
  public void doFilter(ServletRequest request, 
ServletResponse response,
FilterChain chain)
{

if(request.isInitial() || request.getAttribute("com.acme.suspend")==null)
{
// handle intial dispatch
 request.setAttribute("com.acme.suspend",Boolean.TRUE);
request.suspend();
// arrange async callback
return;
}

Boolean suspended=request.getAttribute("com.acme.suspend");
if (suspended)
{
request.setAttribute("com.acme.suspend",Boolean.FALSE);

if(request.isTimeout())
{
// handle timeout
return;
}

// handle resume
}

chain.doFilter(request,response);
}



Add a comment Send a TrackBack