tags:

views:

62

answers:

2
A: 

I would not try to solve this from an event perspective. The event system isn't intended to work that way.

I would define an interface (X) that encapsulates interactions with the server. The implementation, Y, would save the parameters of the last server request. After a timeout occurs, and the user has re-authenticated, then I can ask Y to resend the last request.

Extra benefit: since X is an interface, this simplifies testing as Y can be replaced with a mock object to test the GUI, and test code can call Y without a GUI present.

Update:

Here is an approach that uses SwingWorker to run the server interaction on a bg thread. A ChangeEvent is used to return the results to the EDT for processing. SwingWorker's publish/process are used to handle the user interaction for re-auth. A benefit of SwingWorker is that should the server take a long time to respond, the UI can still respond to repaint events.

class Test extends JPanel {
    private JButton b;
    public Test() {
        b = new JButton(new AbstractAction("Do something") {
            @Override
            public void actionPerformed(ActionEvent e) {
                final JButton btn = (JButton) e.getSource();
                btn.setEnabled(false);
                Object req = new Object(); // Replace w/ apropriate type
                new RequestDispatch(req, new ChangeListener() {
                    @Override
                    public void stateChanged(ChangeEvent e) {
                        final Object req = e.getSource();
                        // Do something with data from 'req'
                        btn.setEnabled(true);
                    }
                });
            }
        });
        add(b);
    }
}

class RequestDispatch extends SwingWorker<Object, Void> {
    private enum DispatchState { Ready, Running, Canceled }
    private final ChangeListener listener;
    private DispatchState dstate = DispatchState.Ready;
    private final Object req;
    RequestDispatch(Object req, ChangeListener listener)
    {
        this.req = req;
        this.listener = listener;
        execute();
    }
    @Override
    protected Object doInBackground() throws Exception {
        while (true) {
            DispatchState st = getDState();
            if (st == DispatchState.Ready)
            {
                try {
                    setDstate(DispatchState.Running);
                    // send request to the server, update req with response
                    return req;
                }
                catch (TimeoutException ex) {
                    this.publish((Void)null);
                    synchronized (this) {
                        wait();
                    }
                }
            }
            if (st == DispatchState.Canceled) {
                return req;
            }
        }
    }
    @Override
    protected void process(List<Void> chunks) {
        // Session has timed out
        // Ask the user to re-authenticate
        int result = JOptionPane.showConfirmDialog(null, "Login");
        if (result == JOptionPane.CANCEL_OPTION) {
            setDstate(DispatchState.Canceled);
        }
        else {
            setDstate(DispatchState.Ready);
        }
    }
    private synchronized DispatchState getDState() {
        return dstate;
    }
    private synchronized void setDstate(DispatchState dstate) {
        this.dstate = dstate;
        notifyAll();
    }
}
Devon_C_Miller
The original design did this. Every communication to the server is wrapped in a message object. The message object being sent to the server was maintained in a variable. After the re-authentication was complete, the message was resent. But the message response was supposed to be delivered to the GUI and refreshed on the screen, which did not happen. Thus the user had no idea that the requested action had taken place and the refreshed data was not visible.While generating a dialog to inform the user about the retry was possible, showing the user the refreshed data would be very difficult.
akhilss
So, the user clicks or types, the event gets dispatched to a listener of some variety which sends a message to the server, reads the response, and updates the display. But, when the timeout occurs, the exception throws you out of that code, so you can resubmit the original message, but you're no longer in the code that knows what to do with the response. Is that about right? Also, are you sending the request to the server from the EDT, or from a background thread?
Devon_C_Miller
Yes, that is about right. The message was sent from the EDT. A custom queue is set as the EDT/system event queue. Every event is delegated to the super class but when an exception occurs a callback is invoked which handles the exception.If you see the code above, assuming the callback handles the exception, I had assumed that I could call super.dispatchEvent(event) and it would redispatch the event triggering the request again and refreshing the screen.
akhilss
A: 

Looking at JDK6 code, AWTEvent.consumed cannot be set to false as there's only consume(), no unconsume().

I think OP's own #5 is the only way, though indeed dangerous. (I actually just learned how to set a private field from AWTEvent#get_InputEvent_CanAccessSystemClipboard().)

In your onError(), you can create another event queue that overrides getNextEvent plus a boolean flag, so that when you call EventQueue#push() the new queue will keep all current events in a list, then do your error notification, finally pop the new queue, flip consumed in the event that threw the exception, redispatch it, followed by the earlier events you saved in the list.

Geoffrey Zheng