Impatient users cause concurrent events

Hello,

Suppose you have a button that closes a window. If the close handler is slow to act, the user can click it twice and execute a second server side event. The first click event completes and closes the window. The second event completes and finds the window closed, or more problematic, finds it null since it doesn’t exist. Maybe this is a contrived example, but you can see the problem.

One solution proposed is to setDisabledOnClick(true) on the button. This works, but requires the programmer to remember to set it, then in the event handler the button may need to be reenabled depending on the behavior of it. This is also a hassle.

Another scenario is that you might have 2 buttons that perform mutually exclusive actions. Clicking one button would disable it and trigger the server action. The other button is still available to be clicked (and it’s action will collide with the first already in progress).

What I really want is for any click (or drag and drop, any event really) to block further interaction until the server responds. This is the way the Zk framework works (we’d like to move to Vaadin from Zk).

Is there some way to do this in Vaadin? I’m currently using Vaadin 6.7.8.

Thanks.

/Daryl

There is no direct way of doing this in Vaadin, sorry. That said, it might be a better default than what we have today - would you care to write a
a feature ticket
for it?

Here’s the ticket:

http://dev.vaadin.com/ticket/10675

Is there some “indirect” way to address this?

/Daryl

You could come close to this, on the server side at least, by locking on the Application object when starting a request and holding the lock until it’s done. Any requests that come in and find the app locked are ignored. This would require writing your own version of AbstractCommunicationManager.doHandleUidlRequest() to ignore requests though.

You could do this by implementing
HttpServletRequestListener
and throw away requests that come in before another is done (these calls aren’t synchronized – see
this discussion
). But only if there were some way to tell Vaadin to ignore a call from within the onRequestStart method – maybe that method should return a boolean. Throwing any Throwable from there will engage the handleServiceException() method in AbstractApplicationServer, but there might be a way to swallow those in the UI so you don’t see them.

Finally, you could add something at the servlet level (e.g. a servlet filter, but that doesn’t tell you when the request is over) to synchronize all access to a particular app object by looking at the session id of the request, but that would synchronize all calls to your app, which isn’t great if you’re serving images and whatever. If you want to try that, I could cook up something. Might be simple to extend AbstractApplicationServlet (which I often do anyway) and override the service() method and either call the super method or return depending on whether or not there’s an ongoing

My real question is what the UI should do when it’s ignoring user interaction. Would you want all the ui widgets to stop responding visually, or would it be good enough to have them look like they’re clicked/dragged/whatever but then they reset as soon as the first request comes back?

Cheers,
Bobby

I meant “…ongoing request.” Anyway, I got curious and here’s a quick attempt at this. Note that it’s going to slow down requests slightly, but there may be a faster way to do this. This is really just a quick attempt at it late in the day. :slight_smile: Extend either ApplicationServlet or AbstractApplicationServlet, depending on whether or not you want to store your Application class name in web.xml or not. Then add:

    // attribute name
    public static final String IN_USE = "in_use";

    // could be any object
    public static final Object SOME_OBJ = new Object();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

        final HttpSession session = request.getSession();

        // this might be faster by using read/write locks, but am doing it
        // simply here.
        synchronized (session) {
            if (session.getAttribute(IN_USE) != null) {
                // ignore this call.
                // Vaadin might need something in response though!
                return;
            }
            session.setAttribute(IN_USE, SOME_OBJ);
        }
        try {
            super.service(request, response);
        } finally {
            synchronized (session) {
                session.removeAttribute(IN_USE);
            }
        }
    }

It
might
work. :slight_smile: Note that you should catch IllegalStateException also since any of the session methods could throw it. But that would make it harder to understand here. There’s also a slight time window such that a request can come in while the response is on it’s way back to the client, but you can hack away at it. Or wait till Vaadin implements it on the client side, heh.

Cheers,
Bobby

Thanks for the ideas, Bobby. I’m not going to get to this right away. I’ll try to remember to post my solution - if I settle on something useful.

/Daryl

Even if you find something not useful, feel free to share, heh heh. That probably describes half of my ideas.

Cheers,
Bobby

Bobby,

I did some experimentation with this, finally. I think my idea of what was happening in Vaadin was a little off. I believe the way I communicated it was technically correct but what I’m after is a little different. I was under the impression that Vaadin was running parallel events. This is not the case best that I can tell. I implement HttpServletRequestListener in my Application. In onRequestStart I set an application boolean field to true, unless it’s already true, then throw an exception. In onRequestEnd I set the boolean to false.

I put 2 buttons on the page each with a Thread.sleep(3000) in the click handler. I found that the click events are queued (i.e. serviced one at a time). The boolean field was never in the “wrong” state (unless I opened another browser window to issue the second click).

It is of course possible the first click action could leave the state of things inappropriate for the second click action, which is what I complained about originally. But that’s not really where my application is failing. I make use of the ConfirmDialog Add-on. This puts up a modal dialog box which is nice, but if there’s a delay in the modal dialog going up, a second click on the same button can put up another. This is because the modal dialog is not blocking the first click event. (Zk does it this way.)

So it appears that I need to find some solution related to ConfirmDialog rather than the Vaadin event queue. In general, when handing an event, gotta make sure the state is appropriate…

I’d still like Vaadin to swallow new events while one is in progress, but it’s no longer a high-priority issue for me. Thanks for your help with this.

/Daryl