DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Efficient API Communication With Spring WebClient
  • Memory Management in Couchbase’s Query Service
  • How to Build Slack App for Audit Requests
  • Idempotency in Distributed Systems: When and Why It Matters

Trending

  • Developers Beware: Slopsquatting and Vibe Coding Can Increase Risk of AI-Powered Attacks
  • Infrastructure as Code (IaC) Beyond the Basics
  • Operational Principles, Architecture, Benefits, and Limitations of Artificial Intelligence Large Language Models
  • Mastering Advanced Traffic Management in Multi-Cloud Kubernetes: Scaling With Multiple Istio Ingress Gateways

Continuations to Continue

By 
Greg Wilkins user avatar
Greg Wilkins
·
Jul. 02, 09 · Interview
Likes (0)
Comment
Save
Tweet
Share
12.3K Views

Join the DZone community and get the full member experience.

Join For Free

Jetty-6 Continuations introduced the concept of asynchronous servlets to provide scalability and quality of service to web 2.0 applications such as chat, collaborative editing, price publishing, as well as powering HTTP based frameworks like cometd, apache camel, openfire XMPP and flex BlazeDS.

With the introduction of similar  asynchronous features in Servlet-3.0, some have suggested that the Continuation API would be deprecated.  Instead, the Continuation API has been updated to provide a simplified portability run asynchronously on any servlet 3.0 container as well as on Jetty (6,7 & 8).  Continuations will work synchronously (blocking) on any 2.5 servlet container. Thus programming to the Continuations API allows your application to achieve asynchronicity today without waiting for the release of stable 3.0 containers (and needing to upgrade all your associated infrastructure).

Continuation Improvements

The old continuation API threw an exception when the continuation was suspended, so that the thread to exit the service method of the servlet/filter. This caused a potential race condition as a continuation would need to be registered with the asynchronous service before the suspend, so that service could do a resume before the actual suspend, unless a common mutex was used.  

Also, the old continuation API had a waiting continuation that would work on non-jetty servers.  However the behaviour of this the waiting continuation was a little different to the normal continuation, so code had to be carefully written to work for both.

The new continuation API does not throw an exception from suspend, so the continuation can be suspended before it is registered with any services and the mutex is no longer needed. With the use of a ContinuationFilter for non asynchronous containers, the continuation will now behaive identically in all servers.

Continuations and Servlet 3.0

The servlet 3.0 asynchronous API introduced some additional asynchronous features not supported by jetty 6 continuations, including:

  • The ability to complete an asynchronous request without dispatching
  • Support for wrapped requests and responses.
  • Listeners for asynchronous events
  • Dispatching asynchronous requests to specific contexts and/or resources

While powerful, these additional features may also be very complicated and confusing. Thus the new Continuation API has cherry picked the good ideas and represents a good compromise between power and complexity.  The servlet 3.0 features adopted are:

  • The completing a continuation without resuming.
  • Support for response wrappers.
  • Optional listeners for asynchronous events.

 

Using The Continuation API

The new continuation API is available in Jetty-7 and is not expected to significantly change in future releases.  Also the continuation library is intended to be deployed in WEB-INF/lib and is portable.  Thus the jetty-7 continuation jar will work asynchronously when deployed in jetty-6, jetty-7, jetty-8 or any servlet 3.0 container.

Obtaining a Continuation

The ContinuationSupport factory class can be used to obtain a continuation instance associated with a request:
    Continuation continuation = ContinuationSupport.getContinuation(request);

Suspending a Request

The suspend a request, the suspend method is called on the continuation:
  void doGet(HttpServletRequest request, HttpServletResponse response)
{
...
continuation.suspend();
...
}
After this method has been called, the lifecycle of the request will be extended beyond the return to the container from the Servlet.service(...) method and Filter.doFilter(...) calls. After these dispatch methods return to, as suspended request will not be committed and a response will not be sent to the HTTP client.

Once a request is suspended, the continuation should be registered with an asynchronous service so that it may be used by an asynchronous callback once the waited for event happens.

The request will be suspended until either continuation.resume() or continuation.complete() is called. If neither is called then the continuation will timeout after a default period or a time set before the suspend by a call to continuation.setTimeout(long). If no timeout listeners resume or complete the continuation, then the continuation is resumed with continuation.isExpired() true. There is a variation of suspend for use with request wrappers and the complete lifecycle (see below):

    continuation.suspend(response);
Suspension is analogous to the servlet 3.0 request.startAsync() method. Unlike jetty-6 continuations, an exception is not thrown by suspend and the method should return normally. This allows the registration of the continuation to occur after suspension and avoids the need for a mutex. If an exception is desirable (to bypass code that is unaware of continuations and may try to commit the response), then continuation.undispatch() may be called to exit the current thread from the current dispatch by throwing a ContinuationThrowable.

Resuming a Request

Once an asynchronous event has occurred, the continuation can be resumed:
  void myAsyncCallback(Object results)
{
continuation.setAttribute("results",results);
continuation.resume();
}
Once a continuation is resumed, the request is redispatched to the servlet container, almost as if the request had been received again. However during the redispatch, the continuation.isInitial() method returns false and any attributes set by the asynchronous handler are available.

Continuation resume is analogous to Servlet 3.0 AsyncContext.dispatch().

Completing Request

As an alternative to completing a request, an asynchronous handler may write the response itself. After writing the response, the handler must indicate the request handling is complete by calling the complete method:
  void myAsyncCallback(Object results)
{
writeResults(continuation.getServletResponse(),results);
continuation.complete();
}
After complete is called, the container schedules the response to be committed and flushed.

Continuation resume is analogous to Servlet 3.0 AsyncContext.complete().

Continuation Listeners

An application may monitor the status of a continuation by using a ContinuationListener:
  void doGet(HttpServletRequest request, HttpServletResponse response)
{
...

Continuation continuation = ContinuationSupport.getContinuation(request);
continuation.addContinuationListener(new ContinuationListener()
{
public void onTimeout(Continuation continuation) { ... }
public void onComplete(Continuation continuation) { ... }
});

continuation.suspend();
...
}

Continuation listeners are analogous to Servlet 3.0 AsyncListeners.

 

Continuation Patterns

Suspend Resume Pattern

The suspend/resume style is used when a servlet and/or filter is used to generate the response after a asynchronous wait that is terminated by an asynchronous handler. Typically a request attribute is used to pass results and to indicate if the request has already been suspended.
  void doGet(HttpServletRequest request, HttpServletResponse response)
{
// if we need to get asynchronous results
Object results = request.getAttribute("results);
if (results==null)
{
final Continuation continuation = ContinuationSupport.getContinuation(request);

// if this is not a timeout
if (continuation.isExpired())
{
sendMyTimeoutResponse(response);
return;
}

// suspend the request
continuation.suspend(); // always suspend before registration

// register with async service. The code here will depend on the
// the service used (see Jetty HttpClient for example)
myAsyncHandler.register(new MyHandler()
{
public void onMyEvent(Object result)
{
continuation.setAttribute("results",results);
continuation.resume();
}
});
return; // or continuation.undispatch();
}

// Send the results
sendMyResultResponse(response,results);
}
This style is very good when the response needs the facilities of the servlet container (eg it uses a web framework) or if the one event may resume many requests so the containers thread pool can be used to handle each of them.

Suspend Continue Pattern

The suspend/complete style is used when an asynchronous handler is used to generate the response:
  void doGet(HttpServletRequest request, HttpServletResponse response)
{
final Continuation continuation = ContinuationSupport.getContinuation(request);

// if this is not a timeout
if (continuation.isExpired())
{
sendMyTimeoutResponse(request,response);
return;
}

// suspend the request
continuation.suspend(response); // response may be wrapped.

// register with async service. The code here will depend on the
// the service used (see Jetty HttpClient for example)
myAsyncHandler.register(new MyHandler()
{
public void onMyEvent(Object result)
{
sendMyResultResponse(continuation.getServletResponse(),results);
continuation.complete();
}
});
}

This style is very good when the response does not needs the facilities of the servlet container (eg it does not use a web framework) and if an event will resume only one continuation. If many responses are to be sent (eg a chat room), then writing one response may block and cause a DOS on the other responses.

 

Continuation Examples

Chat Servlet

The ChatServlet example shows how the suspend/resume style can be used to directly code a chat room. The same principles are applied to frameworks like cometd.org which provide an richer environment for such applications, based on Continuations.

Quality of Service Filter

The QoSFilter(javadoc), uses suspend/resume style to limit the number of requests simultaneously within the filter. This can be used to protect a JDBC connection pool or other limited resource from too many simultaneous requests.

If too many requests are received, the extra requests wait for a short time on a semaphore, before being suspended. As requests within the filter return, they use a priority queue to resume the suspended requests. This allows your authenticated or priority users to get a better share of your servers resources when the machine is under load.

Denial of Service Filter

The DosFilter(javadoc) is similar to the QoSFilter, but protects a web application from a denial of service attack (as best you can from within a web application). If too many requests are detected coming from one source, then those requests are suspended and a warning generated. This works on the assumption that the attacker may be written in simple blocking style, so by suspending you are hopefully consuming their resources. True protection from DOS can only be achieved by network devices (or eugenics :).

Proxy Servlet

The ProxyServlet uses the suspend/complete style and the jetty asynchronous client to implement a scalable Proxy server (or transparent proxy).

Gzip Filter

The jetty GzipFilter is a filter that implements dynamic compression by wrapping the response objects. This filter has been enhanced to understand continuations, so that if a request is suspended in suspend/complete style and the wrapped response is passed to the asynchronous handler, then a ContinuationListener is used to finish the wrapped response. This allows the GzipFilter to work with the asynchronous ProxyServlet and to compress the proxied responses.

 

Where do you get it?

You can read about it, or download it with jetty or include it in your maven project like this pom.xml

From http://blogs.webtide.com

Requests

Opinions expressed by DZone contributors are their own.

Related

  • Efficient API Communication With Spring WebClient
  • Memory Management in Couchbase’s Query Service
  • How to Build Slack App for Audit Requests
  • Idempotency in Distributed Systems: When and Why It Matters

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!