10

I have a Jetty server handling long running HTTP requests- the responses are generated by an a different process X and end up in a collector hash which Jetty requests periodically check.

There are 3 cases:

  1. Process X finishes before the timeout period of the HTTP request - no problem
  2. Process X finishes after the timeout period of the request - no problem
  3. Process X never finishes - below exception occurs

How do I detect this situation (3) and prevent the exception while allowing the other two cases to properly work?

Exception:

2012-06-18 00:13:31.055:WARN:oejut.QueuedThreadPool:
java.lang.IllegalStateException: IDLE,initial
    at org.eclipse.jetty.server.AsyncContinuation.complete(AsyncContinuation.java:569)
    at server.AsyncHTTPRequestProcessor.run(AsyncHTTPRequestProcessor.java:72)
    at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:1119)
    at org.eclipse.jetty.server.AsyncContinuation$1.run(AsyncContinuation.java:875)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:599)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:534)
    at java.lang.Thread.run(Thread.java:679)


Jetty continuation of an HTTP request:

public class AsyncHTTPRequestProcessor implements Runnable {

    private ConcurrentHashMap<String, String> collector;
    private Logger logger;
    private AsyncContext ctx;
    //Defined this here because of strange behaviour when running junit
    //tests and the response json string being empty...
    private String responseStr = null;

    public AsyncHTTPRequestProcessor(AsyncContext _ctx, 
            ConcurrentHashMap<String, String> _collector, Logger _logger) {
        ctx = _ctx;
        collector = _collector;
        logger = _logger;
    }

    @Override
    public void run() {

        logger.info("AsyncContinuation start");

        //if(!((AsyncContinuation)ctx).isInitial()){
        String rid = (String) ctx.getRequest().getAttribute("rid");
        int elapsed = 0;
        if(rid !=null)
        {

            logger.info("AsyncContinuation rid="+rid);

            while(elapsed<ctx.getTimeout())
            {
                if(collector.containsKey(rid)){
                    responseStr = collector.get(rid);
                    collector.remove(rid);

                    logger.info("--->API http request in collector:"+responseStr);
                    ctx.getRequest().setAttribute("status",200);
                    ctx.getRequest().setAttribute("response", responseStr);
                    ctx.getRequest().setAttribute("endTime",System.currentTimeMillis());
                    //ctx.complete();
                    break;
                }
                try {
                    Thread.sleep(10);
                    elapsed+=10;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //}
            logger.info("Collector in async stuff:");
            for(String key:collector.keySet()){
                logger.info(key+"->"+collector.get(key));
            }

            for(Entry<String, String> x:collector.entrySet()){
                logger.info(x.getKey()+"->"+x.getValue());
            }
            ctx.complete(); <---- this line 72
        }
    }

}
informatik01
  • 16,038
  • 10
  • 74
  • 104
nflacco
  • 4,972
  • 8
  • 45
  • 78

2 Answers2

2

The problem here is not your call of AsyncContext#complete() but the overal design of the code.

Continuations (same thing for Servlet async) is designed to be asynchronous. The while loop that makes use of the internal continuations timeout must not be here. You are transforming an asynchronous design to a synchronous one by doing this. The right thing to do is to register a listener using Continuation#addContinuationListener() and implements onTimeout() method to process the timeout case appropriately.

Once your timeout logic is out, I would recommend to move the process X logic to the class AsyncHTTPRequestProcessor and move out from the need of using a collector. During the processing, you should assume that the current thread will never be timed out. By doing this your call to complete() makes sens and you will be immune to concurrency trouble on the collector.

Minh-Triet LÊ
  • 1,374
  • 9
  • 17
0

Using try catch block might help in this case.

   try{
      ctx.complete()
   } catch (IllegalStateException e){
      //Handle it the way you prefer.
   }
harun
  • 1,889
  • 18
  • 19