简体   繁体   中英

“Locker is not reentrant” in asynchronous java jetty servlet long polling

I've got the following situation: I'm trying to implement a long polling servlet in java (will run on jetty). I'm using AsyncContext and TimerTask to achieve this.

I have a session class which has this method:

public boolean setLongPollingContext(final AsyncContext ctx) {
    if (ctx==null)
        return false;
    this.longPollContext = ctx;

    this.alertHandler = new AlertNotificationHandler() {
        @Override
        public void onNewAlert() {

        }

        @Override
        public void onTimeout() {
            System.out.println("*** timeout ***");
            HttpServletResponse response = (HttpServletResponse)ctx.getResponse();
            response.setStatus(408); // timeout
            try {
                response.getWriter().write("Timeout");
            } catch (IOException e) {
                e.printStackTrace();
            }
            ctx.complete();
        }
    };

    this.alertHandler.setTimeout(this.longPollingInterval); // 31 seconds
    return true;
}

(setTimeout just starts a timer thread which calls "onTimeout" in 31 seconds)

I'm calling it like this:

final AsyncContext asyncCtx = request.startAsync(request, response);
session.setLongPollingContext(asyncCtx);

It worked fine under 30 seconds (which is, apparently, the default ssl/http thread timeout in jetty 9.3).

Over 30 seconds I was getting a timeout related issue which I fixed by setting this line in my appease/start.d/ssl.ini file:

## Connector idle timeout in milliseconds
jetty.ssl.idleTimeout=330000

I'm not getting the timeout issue anymore, but now I got something even weirder:

Exception in thread "Timer-0"
java.lang.IllegalStateException: Locker is not reentrant
at org.eclipse.jetty.util.thread.Locker.concLock(Locker.java:85)
at org.eclipse.jetty.util.thread.Locker.lock(Locker.java:61)
at org.eclipse.jetty.server.HttpChannelState.getStatusString(HttpChannelState.java:166)
at org.eclipse.jetty.server.HttpChannelState.complete(HttpChannelState.java:481)
at org.eclipse.jetty.server.AsyncContextState.complete(AsyncContextState.java:92)
at com.theobroma.paranoidandroid.session.ProxyClientSession$1.onTimeout(ProxyClientSession.java:60)
at com.theobroma.paranoidandroid.session.AlertNotificationHandler$1.run(AlertNotificationHandler.java:25)
at java.util.TimerThread.mainLoop(Unknown Source)
at java.util.TimerThread.run(Unknown Source)

Google has failed in giving me any relevant vectors on this.

Is there something else I need to configure in jetty?

Is there a way to set these configuration options without changing config files (from the code, like with annotations or setting some static variables?)

Thanks!

Joakim Erdfelt gave me some really useful hints which I'll compile in an answer, since it fixes the issue for me.

So, it seems that the problem is that the AsyncContext has its own timeout which defaults to 30 seconds on my setup (I'm guessing it has nothing to do with the 30 seconds of jetty, it was just a misleading coincidence)

Once the async context times out, the request and response streams are closed and cannot be opened again, hence the error.

The solution was to increase the timeout of the async context:

ctx.setTimeout(this.longPollingInterval+1000);

I have added some extra time to that to make sure it does not timeout before my own timeout thread.

There is a way to assign a listener for the timeout event to the async context. I will have a look into that and use it instead of my own timeout thread. I'm quite new to Java, so I didn't know about this :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM